0

I have 2 select lists in html, place to enter a number and second place to show the result. It works like a conversion, so when you choose milimeters from the first list and meters from the second it will show you number you entered x 0.001. There is no problem with small numbers (up to 9 digits - I can round these), but when I'm trying to do 1 millimeter to 1 yoctometer (1.0 × 10-21 millimeters) sometimes it is correct, sometimes not. If I add zeros to yoctometer sometimes it will show something like 0.1 + 0.2 = 0.30000000000000004. The problem is I can't round it, because someone can enter like 1000 yoctometers and it won't work. Is there a way to fix this? (look at millimeters -> centimeters, Math.pow)

const config = {
    "milimeters": {
        "milimeters": v => v * 1,
        /* this is only an example */
        "centimeters": v => v * Math.pow(10, -21),
        "decimeters": v => v * 0.01,
        "meters": v => v * 0.001,
        "dekameters": v => v * 0.0001,
        "hectometers": v => v * 0.00001,
        "kilometers": v => v * 0.000001,
        "inches": v => v * 0.0393700787,
        "feet": v => v * 0.0032808399,
        "yards": v => v * 0.0010936133,
        "miles": v => v * 0.000000621371192, 
    },
}

function calculate() {
    const listFromV = document.getElementById("listFrom").value;
    const listToV = document.getElementById("listTo").value;
    const inputPlace = parseFloat(document.getElementById("inputPlace").value);

    const fn = config[listFromV][listToV];
    document.getElementById("resultPlace").innerHTML = fn(inputPlace);

    if (document.getElementById("inputPlace").value == "") {
        document.getElementById("resultPlace").innerHTML = "0.00";
    }

};

Edit:

1 * Math.pow(10, -21) = 1.0000000000000001e-21 /* not correct */
1000 * Math.pow(10, -21) = 1e-18 /* correct */
10 * Math.pow(10, -29) = 1.0000000000000001e-28 /* not correct */
100 * Math.pow(10, -29) = 1e-27 /* correct */
Gibon
  • 89
  • 1
  • 7
  • Try to do all math with integers, then do division at the very end. See [Is floating point math broken?](/q/588004/4642212) and [How to deal with floating point number precision in JavaScript?](/q/1458633/4642212). – Sebastian Simon Dec 29 '21 at 15:59
  • It is not clear where the calculation is going awry, because you talk about `0.1 + 0.2`, but the code shown only multiplies. It does not contain any additions. So how can code that does not have any additions have an error in addition? Also, it is not clear what you mean by “add zeros to yoctometer”. Update the question to show a specific example of input that reproduces a problem and show the observed output and the desired output. – Eric Postpischil Dec 29 '21 at 16:07
  • Floating-point arithmetic is intended to approximate real-number arithmetic. Getting about 1.0000000000000001e-21 for `pow(10, -21)` is expected. Why is this result not satisfactory? It is a tiny error; no physical use of the measurement with that tiny a deviation will have any observable effects. And it can be rounded to 1e-21 for display. – Eric Postpischil Dec 29 '21 at 16:27
  • But it should be exactly 1e-21. It looks bad and it's inaccurate (not much, but calculators shouldn't be inaccurate like this). Is there something that can be done? I can't round it to a certain point, because when I enter e.g. 0.01 it will show 0 as a result. – Gibon Dec 29 '21 at 16:48
  • 1
    Do not round it to a fixed number of places after the decimal point. Round it to a fixed number of significant digits. – Eric Postpischil Dec 29 '21 at 17:24

1 Answers1

0

So there is a way (thanks to Eric Postpischil).

v => Number((v * Math.pow(10, -21)).toPrecision(15))

It works as it should (.toPrecision(15) is the max number that works fine) and it allows up to 14 digits after the point

1 * Math.pow(10, -21) = 1.0000000000000001e-21 /* before */
Number((1 * Math.pow(10, -21)).toPrecision(15)) = 1e-21 /* now */
Gibon
  • 89
  • 1
  • 7