-1

This is the expression I'm having trouble with:

Math.round((Math.ceil((0.4 - 0.1 / 1000) / 0.1) - 1)) * 0.1

In both Chrome and Firefox, it returns 0.30000000000000004. This number needs to be put back into an <input type="number"> element. While Firefox is smart enough to write "0.3", Chrome presents the user the full beauty of JavaScript's inability to do maths properly. Needless to say this is unacceptable from a user's perspective.

Only I can't find a way to make it work correctly. I know that JavaScript is very bad at calculating, but there has to be a solution. I almost don't care about how complicated it is, but the user needs to see "0.3", not a number with 16 decimals when the step is 0.1.

I'd expect – and I've often used it – for Math.round(x / scale) * scale to return a number that is properly rounded to lg(scale) digits. But this has started to fail now.

What magic code is necessary to defeat this evil spell?

PS: The step of 0.1 is an example. It could be anything the user desires. Like 1, 10, 12, 0.25 or 0.5.

ygoe
  • 18,655
  • 23
  • 113
  • 210
  • 4
    JavaScript is no worse than any other language based on IEEE floating point (C, Java, etc). – Pointy Feb 17 '19 at 19:32
  • 1
    "*I've used `Math.round(x / scale) * scale` to return a number that is properly rounded to lg(scale) digits*" - yes, this is working: you get the next best number that is rounded to that step. But it's still a *number*, with floating point accuracy (i.e. not being able to represent 0.3 exactly), and you can use it for further calculations. Converting it to a string for output is a different exercise. – Bergi Feb 17 '19 at 20:06
  • But other languages provide means to handle it. For example, this is possible in C#: `Math.Round(Math.Round((Math.Ceiling((0.4 - 0.1 / 1000) / 0.1) - 1)) * 0.1, 10) == 0.3`; or this: `(decimal)(Math.Round((Math.Ceiling((0.4 - 0.1 / 1000) / 0.1) - 1)) * 0.1)` – ygoe Feb 17 '19 at 20:10
  • 1
    You can't give the step as a plain number. Consider I would pass `1/3` as the step, how many decimals would you need to display that? Not possible. If you want decimal output, you should pass the step as `1 * 10^0`, `1 * 10^1`, `12 * 10^0`, `25 * 10^-2`, `5 * 10^-1` (represented with two separate values, the significant and the exponent). – Bergi Feb 17 '19 at 20:14
  • @Bergi Tell that the HTML specification that defines the `step` attribute. – ygoe Feb 18 '19 at 11:15
  • @ygoe The `step` attribute is a string though, and you can count the number of decimal digits in that string. – Bergi Feb 18 '19 at 11:34

1 Answers1

1

This number needs to be put back into an <input type="number"> element

One solution is

.toFixed(1)
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    This is probably the best idea given that the value is being turned into a string anyway. – Pointy Feb 17 '19 at 19:34
  • Yes, maybe, but I don't know the number of decimal digits. After playing a while with `toFixed` and its related functions (didn't know them), nothing worked universally. So now I'm doing this: `value.toFixed(10).replace(/0+$/, "").replace(/[.,]$/, "")` Since nobody would use more than 10 decimals and all errors seem to take place after that, it should work fine. – ygoe Feb 17 '19 at 19:48
  • @ygoe _"Yes, maybe, but I don't know the number of decimal digits."_ Not sure what you mean. The question asks for a single digit to the right of the decimal to be output, correct? There are numerous procedures which can be used to get expected result, given there are no restrictions at the question as to how output is derived, for example, the decimal can be converted to a string and `RegExp` can be used to match one or more digits followed by dot character followed by one digit character `\`${Math.round((Math.ceil((0.4 - 0.1 / 1000) / 0.1) - 1)) * 0.1}\`.match(/\d+\.\d/)[0]` – guest271314 Feb 17 '19 at 19:49
  • @ygoe What is the expected output when input is _"1, 10, 12"_? `input.value = Number.isInteger(input.valueAsNumber) ? input.value : input.valueAsNumber.toFixed(1)` – guest271314 Feb 17 '19 at 19:51
  • The *step* can be the mentioned examples, not the *value*. If the step is integer, no decimals should be shown. But when the step is not an integer, as many decimals as required to represent the step should be used (at maximum). For a step of 0.25 (which requires 2 decimals), `toFixed(1)` is wrong. I'd need `toFixed(2)`. I couldn't find a comprehensible code using log10 to get me the number of decimals. Or what I found causes more work than string manipulation. Fortunately, this isn't performance-critical. – ygoe Feb 17 '19 at 19:55
  • @ygoe See https://stackoverflow.com/help/how-to-ask, https://stackoverflow.com/help/mcve. All of the possible inputs and expected outputs should be included at the question itself. See the code at the previous comment. Re _"I couldn't find a comprehensible code using log10 to get me the number of decimals."_ see the described use cases, code and explanation at [Number (integer or decimal) to array, array to number (integer or decimal) without using strings](https://stackoverflow.com/questions/54433007/number-integer-or-decimal-to-array-array-to-number-integer-or-decimal-witho) – guest271314 Feb 17 '19 at 19:56