0 votes
by (220 points)

I am using Sugarcube 2 in Twine 1.4.2.

I am running a sequence where the player can buy something with the money they have. As a test, I set the starting money to $50 and the cost of the item as $36.42. I want the screen to display the money, the cost of the item, and the money the player has left after subtracting the cost from the money.

<<set $money = 50.00>>\
<<set $totalprice = 36.42>>\
Total cost: $<<print $totalprice>>
Money you have: $<<print $money>>
Money left to spend: $<<print ($money - $totalprice)>>

However, that returns the following:

Total cost: $36.42
Money you have: $50
Money left to spend: $13.579999999999998

I tried changing the numbers, but I can't figure out why the "money left to spend" displays as such a long number and not $13.58. Even more confusing to me is why it only happens to certain numbers and not others:

$<<print (50.00 - 36.49)>>
$<<print (50.00 - 36.48)>>
$<<print (50.00 - 36.47)>>
$<<print (50.00 - 36.46)>>
$<<print (50.00 - 36.45)>>
$<<print (50.00 - 36.44)>>
$<<print (50.00 - 36.43)>>
$<<print (50.00 - 36.42)>>
$<<print (50.00 - 36.41)>>
$<<print (50.00 - 36.40)>>

This seems like an elementary problem but I can't seem to find anything online about it. Any help will be appreciated, thank you.

1 Answer

+1 vote
by (44.7k points)
selected by
Best answer

The issue is that once you're no longer using whole numbers (integers), computers have a hard time representing decimal places precisely.  You can use the .toFixed() function to compensate for the rounding errors caused by floating point notation (how computers store numbers with decimal places).

For example:

<<print (50.00 - 36.42)>>  /* shows 13.579999999999998 */
<<print (50.00 - 36.42).toFixed(2)>>  /* shows 13.58 */
<<print (50).toFixed(2)>>  /* shows 50.00 */

See .toFixed() for details.

by (220 points)
Thank you very much!
by (159k points)

To overcome this issue in Financial related software systems (like those used in a Bank or a Stock Exchange) it is common to internally store values like 50.00 and 36.42 as Integer values like 5000 and 3642. This allows most of the maths used to also produce integer values, and when they don't the result is converted to such.

When one of those Integer values needs to be displayed they are simple converted back to a 'Decimal like' String value.
eg.  (3642 / 100).toString()

by (8.6k points)

I can only emphasise what greyelf wrote: Always use either integer or fixed-point decimal data types for financial data. Since JavaScript doesn't support fixed-point decimals, that means you have to use integers or rely on external libraries like bignumber.js.