0 votes
by (180 points)

Greetings,

I've made a system that calculates percentages;

<<set $RandomValuePreliminary to 0.29>>
<<set $RandomValueActual to $RandomValuePreliminary * 100>>

The problem therein, is that $RandomValueActual is not simply the value "29", but is in fact " 28.999999999999996 ", which does not coincide with what my calculator says, nor do I think it possible in the first place (I know my math is bad, but I don't think it could be so bad as to return this number). I've tried to rectify the problem with this;

<<set $RandomValueActual to Math.trunc($RandomValueActual)>>

But this actually does not round the value up to the nearest whole number, instead it seems to round it down, in this case back down to 28. This does not happen with every number however, only specific numbers including this one, and seemingly other odd numbers like this one. I'm completely stumped as to an explanation here. Anyone else have anything? I have absolutely no idea. I'd appreciate any help anyone can offer on the subject. Thank you.

3 Answers

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

The floating-point arithmetic used in modern computers (see: IEEE 754) is an approximation which trades precision for range.  If you want floating-point results, but require more accuracy than it gives you, then you're better off using integers (whole numbers) until you absolutely need to convert to floating-point.

In particular, if you simply need to shift the decimal place around, then I'd probably recommend using scientific E-notation, rather than using arithmetic at all (i.e. no multiplication or division by a multiple of 10, since that's vulnerable to approximation).  For example:

<<set $preliminaryValue to 0.29>>
<<set $actualValue to Number($preliminaryValue + 'e2')>>

What that does is convert the number 0.29 to the scientific E-notation 0.29e2 and then converts that into the number 29.

The reverse would be something like the following:

<<set $preliminaryValue to 29>>
<<set $actualValue to Number($preliminaryValue + 'e-2')>>

 

by (180 points)
Brilliant! Thank you very much, that's solved the issue! Seems a bit dodgy that maths calculation would need to trade range over precision, as technically speaking surely there's only one correct answer to arithmetic questions? Seems odd.. Never heard of this E-notation before either, but seems useful!
by (68.6k points)

There are two basic issues in play:

  1. You cannot represent some real numbers exactly in decimal (i.e. base-10).  Two quick examples: the irrational mathematical constant Pi and the rational number 1/3.  Both infinitely repeat in decimal (see: repeating decimals).
  2. JavaScript's Number type is backed by a 64-bit floating-point type and some real number arithmetic requires more range, in bits, than the type system has bits to spare.  For example, the result of an arithmetic operation might require 96-bits, or even more, for full accuracy.

Both of these issues mean that, at least some, decimal number types must support approximation, and IEEE 754 floating-point is the standard most computers and languages use today for that purpose.

by (180 points)
Ahh, thanks for the explanation! Would there happen to be a similar method of using the E notation to add rather than multiply? I believe I may be getting the same type of result from adding two values together; the result being a value with multiple decimal points which of course should not be possible under regular arithmetic conditions. I can always post it as a new question if necessary, but judging how it's similar in nature to this problem, I was wondering if there was a similar resolution available like the one available to this question.
by (68.6k points)

Would there happen to be a similar method of using the E notation to add rather than multiply?

No.  JavaScript's handling of the E-notation style of scientific notation merely allows you to shift the decimal place of a number—analogous to multiplying or dividing by a power of 10, but without actually doing the arithmetic.

 

I believe I may be getting the same type of result from adding two values together; the result being a value with multiple decimal points which of course should not be possible under regular arithmetic conditions.

That sounds odd.  It's definitely possible to add two numbers causing an approximation, however, that would require fairly specific circumstances—I'm primarily thinking of range issues.  Can you give an example of what values you're adding and how?

 

I can always post it as a new question if necessary, but judging how it's similar in nature to this problem, I was wondering if there was a similar resolution available like the one available to this question.

It is tangential to your originally posted question, so I'd say we can discuss it a bit more here first.  If it proves to be a significant question in its own right, you can always use it as the basis of a new question then.  Or do so now, if that's your preference.

by (180 points)
I did a little experimentation and stress testing in my own time to see what I can find. As usual, I'm always a little embarrassed to find the issue has usually been hiding in plain sight or buried under my nose for that entire time, if it even is the crux of the problem in the first place. I'm creating a new question for it though, as the nature of it is slightly different, but still in a somewhat similar vein.
0 votes
by (63.1k points)
edited by

Floating point numbers are not accurate in JavaScript, which causes the discrepancy. More info. Generally speaking, you should use integers until you can't, especially if it's important to get accurate results. 

For example: 

0.1 + 0.2 = 0.3000...4

(1 + 2) / 10 = 0.3

<<if 0.1 + 0.2 is 0.3>>
  this won't show
<</if>>
<<if ((1 + 2) / 10) is 0.3>>
  this will show 
<</if>>

Edit: see below. 

by (68.6k points)
As I write this, your second closing <<if>> tag is using a backslash instead of the proper forward slash.
by (63.1k points)
Oops. Thanks.
by (180 points)
Interesting.. I'll keep that fact in mind for the future. Thank you kindly
0 votes
by (159k points)

To add to what @Chapel stated:

You were trying to us the Javascript Math.trunc() function which as it's documentation explains just truncates anything after the decimal point. You need to use the Math.round() function which rounds the value passed to the nearest integer instead.

<<set $RandomValueActual to Math.round($RandomValueActual)>>

 

by (180 points)
Hey thanks! I couldn't find that one on the motoslave.net site.. I'm guessing it wouldn't necessarily account for the dodgy maths solving though, so it could provide inaccurate answers still, correct?
...