basic game maths function and number formats explanation please

Freeform discussion about anything related to modding Transcendence.
Post Reply
relanat
Militia Captain
Militia Captain
Posts: 941
Joined: Tue Nov 05, 2013 9:56 am

Last time I checked dividing 1 by 10 gave 0.1.
But according to the debug console it is actually zero or 0.10000000000000001.
This is from dividing the item mass of a ROM by 10. Using the 'divide' function returns 0 and using '/' gives the longer result.

Could someone provide some sort of explanation about which of the paired functions (eg, * and multiply) to use to get 'normal' results? This is messing up cost calculations in a mod. If the different number formats, int32, real, integer, etc, could also be briefly explained that would be appreciated.

I am using item masses, item counts and topology distances as well as an adjusting variable which fine tunes the results to give shipping costs for items to other systems.
TIA.
Stupid code. Do what I want, not what I typed in!
NMS
Militia Captain
Militia Captain
Posts: 569
Joined: Tue Mar 05, 2013 8:26 am

The short answer is that add, subtract, multiply, and divide all work only with integers (called "int32" by typeOf), while +, -, *, and / work with floating point numbers ("real").

The int32 type can hold integers from -2147483648 (0x80000000) to 2147483647 (0x7FFFFFFF). They give exact results as long as you stay in this range and don't need to use fractions. The older integer functions convert their arguments to int32 by truncating, so for example:
(add 1.5 1.8) -> 2

The real type can hold numbers between negative and positive 2^1024 (exclusive) - about 1.797 * 10^308. But floating point numbers have limited precision. In this case it's about 17 decimal places, but they're actually using binary, so they're a series of powers of 2 added together. This means they can't represent values like 1/10 exactly. As you saw, this can result in small errors. Sometimes these errors can become significant, such as when you subtract two numbers that are very close to equal. But usually they're just inconvenient. You shouldn't check whether the results of calculations with reals are equal. If necessary, you can check whether the absolute value of their difference is below some much smaller value.

Unfortunately, there's no convenient way to do perfectly accurate calculations with decimals. If perfect accuracy is important, but you only use a certain number of decimals, you can multiply the values by some power of 10 and do math with integers. For instance, the Corporate Command stock exchange stores stock prices in centicredits. Then you can use divide and modulo to get the whole number and decimal remainder. But if the remainder is small, you have to insert zeroes between the decimal point and it. There should probably be a helper function for this. Another option is to work with floating point numbers, but use round when you need an integer or fmtNumber to display the result. (fmtNumber 'real ...) will show up to 2 decimal places for values under 1, up to 1 decimal place for values between 1 and 100 (exclusive), and no decimals for values 100 and up.

There are also differences in certain cases between gr and >, eq and =, etc. But they don't matter as often and I'm not sure exactly what they are or whether they're intentional.
relanat
Militia Captain
Militia Captain
Posts: 941
Joined: Tue Nov 05, 2013 9:56 am

Thanks. Excellent info as always.

I recall gunship256 having a similar sort of problem but couldn't find the topic. I can work with this to get the desired result. A big thumbs up!

Also, while looking at something completely unrelated, I saw these in Code.xml:

Code: Select all

(setq intRoundDown (lambda (value multiple)
	(multiply (divide value multiple) multiple)
	))

(setq intRoundUp (lambda (value multiple)
	(multiply (divide (add value (subtract multiple 1)) multiple) multiple)
	))
If they haven't been deprecated they may be of use to someone. Just looking at the code gives me a headache!
Stupid code. Do what I want, not what I typed in!
relanat
Militia Captain
Militia Captain
Posts: 941
Joined: Tue Nov 05, 2013 9:56 am

Thanks again for this. It is much easier to fix bugs when you understand what might be happening to cause the error. Still painful though!

For info, an integer is a whole number. A number without a decimal point and not a fraction. So 3 or 145,682 are integers but 3.1, 145,682.00453 and 1/10 are not. Integers can also be negative, eg -1 or -2,045.
And truncating means chopping the end off. So, to use NMS's example above 1.5 truncates to 1 and 1.8 also truncates to 1. So '(add 1.5 1.8)' is the same as '(add 1 1)' which is 2. You can also truncate to a number of decimals. So 1.3456 and 76.8765 could be truncated to 1.34 and 76.87.

A couple more functions which can be helpful.

From the function list.
Rounding functions:
(ceil x) -> x rounded up, i.e. towards positive infinity (real)
--------
(floor x) -> x rounded down, i.e. towards negative infinity (real)
--------
(round ['stochastic] x) -> y
The 'stochastic' option for 'round' slightly alters the rounding point. Anyone know when this would be used?

Conversion functions:
(convertTo dataType value) -> result

dataType: 'error|'int32|'list|'nil|'real|'string|'true
--------
(fmtNumber [type] value) -> string

type:

'integer
'power
'real
'regenRate
'speed
--------
(int x) -> x as an integer
--------
And for checking:
(typeOf item) -> type

type:

'error
'function
'int32
'list
'nil
'primitive
'real
'string
'struct
'true
Also 'exp' and 'double' but I have no idea what they do. They aren't used in Transcendence_Source.
Last edited by relanat on Wed Jun 19, 2019 5:45 am, edited 1 time in total.
Stupid code. Do what I want, not what I typed in!
NMS
Militia Captain
Militia Captain
Posts: 569
Joined: Tue Mar 05, 2013 8:26 am

Stochastic rounding randomly rounds up or down based on how large the remainder is. So 1.6 has a 60% chance of rounding up, 40% of rounding down. This keeps the expected average value of the output equal to the input.
relanat
Militia Captain
Militia Captain
Posts: 941
Joined: Tue Nov 05, 2013 9:56 am

Thanks.

For info, 'fmtCurrency' only works with integers.
To guarantee a result convert numbers to 'int32' format.

Code: Select all

(fmtCurrency 'credit (convertTo 'int32 currencyNumber))
'strMassString' works with both integers and reals.
Stupid code. Do what I want, not what I typed in!
Post Reply