Changeset - r27297:7606e4982c9b
[Not reviewed]
master
0 1 0
Peter Nelson - 18 months ago 2023-05-08 22:00:24
peter1138@openttd.org
Change: Perform unit convert with a double-precision constant.

Unit conversion is only performed for display purposes, this does not
affect lock-step mechanics.

This replaces the old multiply and shift algorithm which relies on
choosing a multipler and shift combination that gets close. Some of these
multiply/shift combinations were quite inaccurate. We can just
use (close-to) real-world numbers instead.
1 file changed with 36 insertions and 33 deletions:
0 comments (0 inline, 0 general)
src/strings.cpp
Show inline comments
 
@@ -637,144 +637,147 @@ static int DeterminePluralForm(int64 cou
 
		 *   Romanian */
 
		case 14:
 
			return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
 
	}
 
}
 

	
 
static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
 
{
 
	/* <NUM> {Length of each string} {each string} */
 
	uint n = (byte)*b++;
 
	uint pos, i, mypos = 0;
 

	
 
	for (i = pos = 0; i != n; i++) {
 
		uint len = (byte)*b++;
 
		if (i == form) mypos = pos;
 
		pos += len;
 
	}
 

	
 
	*dst += seprintf(*dst, last, "%s", b + mypos);
 
	return b + pos;
 
}
 

	
 
/** Helper for unit conversion. */
 
struct UnitConversion {
 
	int multiplier; ///< Amount to multiply upon conversion.
 
	int shift;      ///< Amount to shift upon conversion.
 
	double factor; ///< Amount to multiply or divide upon conversion.
 

	
 
	/**
 
	 * Convert value from OpenTTD's internal unit into the displayed value.
 
	 * @param input The input to convert.
 
	 * @param round Whether to round the value or not.
 
	 * @return The converted value.
 
	 */
 
	int64 ToDisplay(int64 input, bool round = true) const
 
	{
 
		return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
 
		return round
 
			? (int64_t)std::round(input * this->factor)
 
			: (int64_t)(input * this->factor);
 
	}
 

	
 
	/**
 
	 * Convert the displayed value back into a value of OpenTTD's internal unit.
 
	 * @param input The input to convert.
 
	 * @param round Whether to round the value up or not.
 
	 * @param divider Divide the return value by this.
 
	 * @return The converted value.
 
	 */
 
	int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
 
	{
 
		return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
 
		return round
 
			? (int64_t)std::round(input / this->factor / divider)
 
			: (int64_t)(input / this->factor / divider);
 
	}
 
};
 

	
 
/** Information about a specific unit system. */
 
struct Units {
 
	UnitConversion c; ///< Conversion
 
	StringID s;       ///< String for the unit
 
	unsigned int decimal_places; ///< Number of decimal places embedded in the value. For example, 1 if the value is in tenths, and 3 if the value is in thousandths.
 
};
 

	
 
/** Information about a specific unit system with a long variant. */
 
struct UnitsLong {
 
	UnitConversion c; ///< Conversion
 
	StringID s;       ///< String for the short variant of the unit
 
	StringID l;       ///< String for the long variant of the unit
 
};
 

	
 
/** Unit conversions for velocity. */
 
static const Units _units_velocity[] = {
 
	{ {       1,  0}, STR_UNITS_VELOCITY_IMPERIAL,     0 },
 
	{ {     103,  6}, STR_UNITS_VELOCITY_METRIC,       0 },
 
	{ {    1831, 12}, STR_UNITS_VELOCITY_SI,           0 },
 
	{ {   37888, 16}, STR_UNITS_VELOCITY_GAMEUNITS,    1 },
 
	{ { 7289499, 23}, STR_UNITS_VELOCITY_KNOTS,        0 },
 
	{ { 1.0      }, STR_UNITS_VELOCITY_IMPERIAL,  0 },
 
	{ { 1.609344 }, STR_UNITS_VELOCITY_METRIC,    0 },
 
	{ { 0.44704  }, STR_UNITS_VELOCITY_SI,        0 },
 
	{ { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS, 1 },
 
	{ { 0.868976 }, STR_UNITS_VELOCITY_KNOTS,     0 },
 
};
 

	
 
/** Unit conversions for power. */
 
static const Units _units_power[] = {
 
	{ {   1,  0}, STR_UNITS_POWER_IMPERIAL, 0 },
 
	{ {4153, 12}, STR_UNITS_POWER_METRIC,   0 },
 
	{ {6109, 13}, STR_UNITS_POWER_SI,       0 },
 
	{ { 1.0      }, STR_UNITS_POWER_IMPERIAL, 0 },
 
	{ { 1.01387  }, STR_UNITS_POWER_METRIC,   0 },
 
	{ { 0.745699 }, STR_UNITS_POWER_SI,       0 },
 
};
 

	
 
/** Unit conversions for power to weight. */
 
static const Units _units_power_to_weight[] = {
 
	{ {  29,  5}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1},
 
	{ {   1,  0}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC, 1},
 
	{ {   1,  0}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI, 1},
 
	{ {  59,  6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL, 1},
 
	{ {  65,  6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC, 1},
 
	{ {  65,  6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI, 1},
 
	{ { 173,  8}, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL, 1},
 
	{ {   3,  2}, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC, 1},
 
	{ {   3,  2}, STR_UNITS_POWER_SI_TO_WEIGHT_SI, 1},
 
	{ { 0.907185 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1 },
 
	{ { 1.0      }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC,   1 },
 
	{ { 1.0      }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI,       1 },
 
	{ { 0.919768 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL,   1 },
 
	{ { 1.01387  }, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC,     1 },
 
	{ { 1.01387  }, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI,         1 },
 
	{ { 0.676487 }, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL,       1 },
 
	{ { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC,         1 },
 
	{ { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_SI,             1 },
 
};
 

	
 
/** Unit conversions for weight. */
 
static const UnitsLong _units_weight[] = {
 
	{ {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
 
	{ {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC,   STR_UNITS_WEIGHT_LONG_METRIC   },
 
	{ {1000,  0}, STR_UNITS_WEIGHT_SHORT_SI,       STR_UNITS_WEIGHT_LONG_SI       },
 
	{ {    1.102311 }, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
 
	{ {    1.0      }, STR_UNITS_WEIGHT_SHORT_METRIC,   STR_UNITS_WEIGHT_LONG_METRIC   },
 
	{ { 1000.0      }, STR_UNITS_WEIGHT_SHORT_SI,       STR_UNITS_WEIGHT_LONG_SI       },
 
};
 

	
 
/** Unit conversions for volume. */
 
static const UnitsLong _units_volume[] = {
 
	{ {4227,  4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
 
	{ {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC,   STR_UNITS_VOLUME_LONG_METRIC   },
 
	{ {   1,  0}, STR_UNITS_VOLUME_SHORT_SI,       STR_UNITS_VOLUME_LONG_SI       },
 
	{ {  264.172 }, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
 
	{ { 1000.0   }, STR_UNITS_VOLUME_SHORT_METRIC,   STR_UNITS_VOLUME_LONG_METRIC   },
 
	{ {    1.0   }, STR_UNITS_VOLUME_SHORT_SI,       STR_UNITS_VOLUME_LONG_SI       },
 
};
 

	
 
/** Unit conversions for force. */
 
static const Units _units_force[] = {
 
	{ {3597,  4}, STR_UNITS_FORCE_IMPERIAL, 0 },
 
	{ {3263,  5}, STR_UNITS_FORCE_METRIC,   0 },
 
	{ {   1,  0}, STR_UNITS_FORCE_SI,       0 },
 
	{ { 224.809 }, STR_UNITS_FORCE_IMPERIAL, 0 },
 
	{ { 101.972 }, STR_UNITS_FORCE_METRIC,   0 },
 
	{ {   1.0   }, STR_UNITS_FORCE_SI,       0 },
 
};
 

	
 
/** Unit conversions for height. */
 
static const Units _units_height[] = {
 
	{ {   3,  0}, STR_UNITS_HEIGHT_IMPERIAL, 0 }, // "Wrong" conversion factor for more nicer GUI values
 
	{ {   1,  0}, STR_UNITS_HEIGHT_METRIC,   0 },
 
	{ {   1,  0}, STR_UNITS_HEIGHT_SI,       0 },
 
	{ { 3.0 }, STR_UNITS_HEIGHT_IMPERIAL, 0 }, // "Wrong" conversion factor for more nicer GUI values
 
	{ { 1.0 }, STR_UNITS_HEIGHT_METRIC,   0 },
 
	{ { 1.0 }, STR_UNITS_HEIGHT_SI,       0 },
 
};
 

	
 
/**
 
 * Get index for velocity conversion units for a vehicle type.
 
 * @param type VehicleType to convert velocity for.
 
 * @return Index within velocity conversion units for vehicle type.
 
 */
 
static byte GetVelocityUnits(VehicleType type)
 
{
 
	if (type == VEH_SHIP || type == VEH_AIRCRAFT) return _settings_game.locale.units_velocity_nautical;
 

	
 
	return _settings_game.locale.units_velocity;
 
}
 

	
 
/**
 
 * Convert the given (internal) speed to the display speed.
 
 * @param speed the speed to convert
 
 * @return the converted speed.
 
 */
 
uint ConvertSpeedToDisplaySpeed(uint speed, VehicleType type)
 
{
 
	/* For historical reasons we don't want to mess with the
 
	 * conversion for speed. So, don't round it and keep the
 
	 * original conversion factors instead of the real ones. */
0 comments (0 inline, 0 general)