Math in JS.

Can anyone please go through the code and let me know why converting from dollar to cents gives wrong result Steps: $32.5:Converting to cent->32*100=3200. value after decimal point is cent =0005. $10 to be charged extra if cost is less than $40 so $10 gets converted into cents as =1000 Summing up all the above three values we get in cents=4205. convert back to dollars=4205/100=$42.05.This is the process used to convert manually. but in reality ans should be $42.5. https://codepen.io/kvandana451/pen/wvLjBEa
89 Replies
Chris Bolson
Chris Bolson5mo ago
There are a few issues with your code but my main question is, why do you even need to split it? something like this would return the value that you want with out any need to extract the decimal value:
function calc(){
let num = Number(document.querySelector('input').value);
if (num < 40) {
num += 10;
}
document.querySelector('.total').innerText = `$${num}`;
}
function calc(){
let num = Number(document.querySelector('input').value);
if (num < 40) {
num += 10;
}
document.querySelector('.total').innerText = `$${num}`;
}
Vandana
VandanaOP5mo ago
For floating point nos to avoid inaccuracies.
Chris Bolson
Chris Bolson5mo ago
what inaccuracies are you envisaging? Either the number is below 40 or it isn't, with or without decimals.
Vandana
VandanaOP5mo ago
32.5
Chris Bolson
Chris Bolson5mo ago
"32.5" would work fine with the code that I posted.
Vandana
VandanaOP5mo ago
yes. But its suggested to convert float number into cents to prevent inaccuracies Manually we do it like this with pen and paper. 7.99 gives 17.9900002
Chris Bolson
Chris Bolson5mo ago
Ok, I see
kirin-808
kirin-8085mo ago
You probably want .toFixed() to handle that kind of stuff. There's a good example on MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
MDN Web Docs
Number.prototype.toFixed() - JavaScript | MDN
The toFixed() method of Number values returns a string representing this number using fixed-point notation with the specified number of decimal places.
kirin-808
kirin-8085mo ago
Since the problem lies in the display of the value, and not the value itself.
C_7
C_75mo ago
There are two errors in this function 1. Obtaining the value of decResult 2. Calculation formula for <40 First, let’s talk about problem 1: You will find that your input may have one or two decimal places. When there are two decimal places, the value calculated by decResult is correct. When there is one decimal place, decResult is obviously incorrect, which is why the final result is wrong ————————————————————————————— Decimal part Actual Expected ————————————————————————————— 0.99 99 99 0.05 5 5 0.5 5 50 ————————————————————————————— Let’s talk about problem 2: The following is your code
(wholeNum * 100 + decResult) / 100 + 10
// Take 7.99 as an example
// 7 * 100 === 700
// 700 + 99 === 799
// 799 / 100 === 7.99
// 7.99 + 10 === 17.990000000000002
(wholeNum * 100 + decResult) / 100 + 10
// Take 7.99 as an example
// 7 * 100 === 700
// 700 + 99 === 799
// 799 / 100 === 7.99
// 7.99 + 10 === 17.990000000000002
You will find that your calculation formula is the same as directly [num + 10], there is no difference, because you finally take a floating point number for calculation, instead of converting it into a floating point number that is 100 times smaller after the calculation is completed As Kirin-808 said, You probably want .toFixed() to handle that kind of stuff.
function calc() {
const num = Number(document.querySelector("input").value);
const totalElement = document.querySelector(".total");

// num is not a number
if (isNaN(num)) {
window.alert("user input is not a number");
return;
}

// free
if (num >= 40) {
totalElement.innerHTML = `$${num}`;
return;
}

// $10 shipping
const res = Number.parseFloat(num + 10).toFixed(2);
totalElement.innerHTML = `$${res}`;
}
function calc() {
const num = Number(document.querySelector("input").value);
const totalElement = document.querySelector(".total");

// num is not a number
if (isNaN(num)) {
window.alert("user input is not a number");
return;
}

// free
if (num >= 40) {
totalElement.innerHTML = `$${num}`;
return;
}

// $10 shipping
const res = Number.parseFloat(num + 10).toFixed(2);
totalElement.innerHTML = `$${res}`;
}
Choo♚𝕂𝕚𝕟𝕘
const res = Number.parseFloat(num + 10).toFixed(2);
const res = Number.parseFloat(num + 10).toFixed(2);
This is not correct. The + happens BEFORE the parse. This results in concatenation and the parse happens AFTER that. Consider for example if num is "5". What happens is ("5" + 10) is calculated before parsing, and the result of that is the string "510". Parsing that would be the number 510 instead of 15.
ἔρως
ἔρως5mo ago
how about you don't do any parsing? first, make sure it only has 2 decimal places: value.replace(/\.(\d\d?)?.*$/, '.$1')
C_7
C_75mo ago
emm.... This should not happen, because the first line of the function performs the number conversion operation.
const num = Number(document.querySelector("input").value);
const num = Number(document.querySelector("input").value);
ἔρως
ἔρως5mo ago
that's horrible, and obliterates accuracy if you use floats at all, at any point, it's ruined you have to handle it as a string, to do not lose accuracy
let value = '12345.6';

let new_value = value
.replace(/(?:[.,]0|[.,]?)$/, '.00')
.replace(/[.,](\d\d?)?.*$/, '.$1')
.replace(/[.,](\d)$/, '.$10')
.replace(/[.,]/, '');

console.log(new_value);
console.log(parseInt(new_value, 10));
let value = '12345.6';

let new_value = value
.replace(/(?:[.,]0|[.,]?)$/, '.00')
.replace(/[.,](\d\d?)?.*$/, '.$1')
.replace(/[.,](\d)$/, '.$10')
.replace(/[.,]/, '');

console.log(new_value);
console.log(parseInt(new_value, 10));
this is a solution that parses the number as a string step 1: .replace(/(?:[.,]0|[.,]?)$/, '.00') - for something like 12 or 12. or 12.0, makes it into 12.00 - for something like 12.00, makes it into 12.00.00 (for performance reasons) step 2: .replace(/[.,](\d\d?)?.*$/, '.$1') - for something like 12.00 or 12.00.00, makes it into 12.00 - for something like 12.3, makes it into 12.3 - for something like 12.3456789 or 12.344444444444, makes it into 12.34 step 3: .replace(/[.,](\d)$/, '.$10') - for something like 12.3, makes it into 12.30 - for something like 12.00, makes it into 12.00 step 4: remove decimal point E.g.: - 12.00 > 12.00.00 > 12.00 > 1200 - 12345.6 > 12345.6 > 12345.60 > 1234560 - 9876.54321 > 9876.54321 > 9876.54 > 987654 - 1 > 1.00 > 1.00 > 100 with absolutely 0 precision loss, converts a decimal number to an integer one multiplied by 100
Choo♚𝕂𝕚𝕟𝕘
OK. I didn't notice that Number conversion. If that happens, then the parse is completely unnecessary.
ἔρως
ἔρως5mo ago
i found a simpler method:
let value = '12345.67';

let parts = value.split(/[.,]/);

let new_value = (parseInt(parts[0] ?? '0', 10) * 100) + parseInt((parts[1] ?? '0').toString().substring(0, 2).padEnd(2, '0'), 10);


console.log(new_value);
console.log(parseInt(new_value, 10));
let value = '12345.67';

let parts = value.split(/[.,]/);

let new_value = (parseInt(parts[0] ?? '0', 10) * 100) + parseInt((parts[1] ?? '0').toString().substring(0, 2).padEnd(2, '0'), 10);


console.log(new_value);
console.log(parseInt(new_value, 10));
Vandana
VandanaOP5mo ago
Another simple method which can be understandable?
ἔρως
ἔρως5mo ago
that is splitting the string by the decimal separator, then multiplies the left side by 100, makes the right side be between 0 and 99 and adds it to the left side without going into precision loss, i cant think of something better
Vandana
VandanaOP5mo ago
You mean this?
ἔρως
ἔρως5mo ago
yes, that it parses the decimal dollar value into cents and with cents, you can do your math if you use decimals, you're just doing it wrong just remember the classic 0.1 + 0.2 not being the same as 0.3
Jochem
Jochem5mo ago
you can just Math.round(parseFloat('1234.56') * 100)
ἔρως
ἔρως5mo ago
that parses as a float already has innacuracies
Jochem
Jochem5mo ago
not enough of an inaccuracy to change the final value round returns an int
ἔρως
ἔρως5mo ago
but probably will be ok for 99.9999% of the times
Jochem
Jochem5mo ago
no, 100%. There's not a single reasonable situation where a float is so inaccurate that it's off by 0.005 when you're dealing with currency at least
ἔρως
ἔρως5mo ago
ive had troubles with rounding errors with 2 decimal places and even with 4, but 4 helped a lot
Jochem
Jochem5mo ago
when you start adding things as floats sure, but with a single conversion at the start?
ἔρως
ἔρως5mo ago
it's possible im going overkill and skipping all possibilities
Jochem
Jochem5mo ago
hmm... then use a regex to validate that there's two decimal places and .replace the point out, then parseInt 😄
ἔρως
ἔρως5mo ago
this does that, but without regex this does that, with regex and support for comma or period for decimals both options guarantee that you have 2 decimal places, and then return an usable integer number none do rounding, only truncating
Jochem
Jochem5mo ago
yeah, fair. It's still massive overkill for almost all situations at worst it'll be off by 1 cent and that's only if you're talking about billions of dollars, if not trillions
ἔρως
ἔρως5mo ago
yes, but 1 cent is enough to be wrong
Jochem
Jochem5mo ago
no one gives a shit about 1c if you're paying a trillion dollars for something
ἔρως
ἔρως5mo ago
but ... you are right, it is overkill for this situation still the correct way!
Jochem
Jochem5mo ago
For reference, rounding a parsefloat*100 is what I've used for software that's processed over 3M invoices and it's never gone wrong and that includes invoices north of $1M
ἔρως
ἔρως5mo ago
i would be scared as hell of using any of these solutions but, i cant deny it: it works
Jochem
Jochem5mo ago
(and again, no one gives a shit about 1c in the accounting world)
ἔρως
ἔρως5mo ago
you sure? 🤔
Jochem
Jochem5mo ago
no one I've worked with at least there's a ton of things where you get say... 21% VAT, and you have a net and gross and VAT amount
ἔρως
ἔρως5mo ago
i think companies have a percentage of error that is acceptable, without paying fines, right?
Jochem
Jochem5mo ago
there's situations where net and 21% net don't add up to a whole cent value. Some invoicing software uses floor, others ceil, or round. Or the net is calculated from the gross, because $25 looks a lot better on a price sticker than $25.03 and then there's occasionally discount amounts for paying on time, so say another 7% off if you pay by the due date, and there's rounding-cents lost everywhere so yeah, no one cares if you pay $0.01 too much or too little
ἔρως
ἔρως5mo ago
alright, that makes sense
Jochem
Jochem5mo ago
yeah, that too. My dad tried rounding down to the nearest whole € for a while for all the invoices he paid, no one cared so yeah, round(parsefloat() * 100) is more than fine for pretty much anything to do with currency just minimize the amount of math you do with floats
ἔρως
ἔρως5mo ago
yes, minimize it to 0 no math with floats, if you need precision or use fixed-point decimals, if they are fast like mysql's decimal thingy
missymae
missymae5mo ago
I didn't read this thread closely, but that greedy dot star set off alarms capture the optional 2 characters first I guess, avoid the performance hit of parsing an n-length number, if that's the only risk...
ἔρως
ἔρως5mo ago
it's just to truncate to 2 decimal numbers
missymae
missymae5mo ago
yes
ἔρως
ἔρως5mo ago
if you have 0.05892897298217329832178129387313289312781313982712313289123713289132781321839132713291327891327132897132, you get 0.05 it's not rounding: it's truncating
missymae
missymae5mo ago
yes, but the regex engine will still evaluate the rest of the string, which could be any length
ἔρως
ἔρως5mo ago
yes, it will it has to, sadly which is why i found a better solution
missymae
missymae5mo ago
and if it's a very long number it's a performance hit, or a malicious way to slow a program.
ἔρως
ἔρως5mo ago
yes, it is
missymae
missymae5mo ago
regex is the best (not literally, just ❤️ ) , so dropping regex for a readable, accurate, safe solution isn't really better... it's just better.
ἔρως
ἔρως5mo ago
basically, the string will have to be about 20000 characters long to take 8.8ms to parse the number i love regex, so, i went first with a regex solution
missymae
missymae5mo ago
Holy F JS has a large string size limit, chrome allows a 2^29 - 24 elements (~1 GiB) I imagined you would also be a regex fan 🙂
ἔρως
ἔρως5mo ago
no no, not a fan: a lover i do some cursed stuff with it too, like, validating numbers
Vandana
VandanaOP4mo ago
without regex. I didnt use parse, i used Number to convert the string to number.Kindly go through. .. kept this in mind. @ἔρως @Jochem waiting for the update.
glutonium
glutonium4mo ago
function calc() {
let num = Number(document.querySelector("input").value);
let errorEle = document.querySelector(".error-msg");
let total = document.querySelector(".total");
errorEle.innerText = "";
total.innerText = "";

if (num < 0) {

errorEle.innerText = `Error:cost cant be less than $0`;
errorEle.classList.add("error-text");

return;
}

if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}

// Number("32.50".substring(3))
}
function calc() {
let num = Number(document.querySelector("input").value);
let errorEle = document.querySelector(".error-msg");
let total = document.querySelector(".total");
errorEle.innerText = "";
total.innerText = "";

if (num < 0) {

errorEle.innerText = `Error:cost cant be less than $0`;
errorEle.classList.add("error-text");

return;
}

if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}

// Number("32.50".substring(3))
}
ἔρως
ἔρως4mo ago
that's not going to work properly
if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}
if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}
^ this has a bug: you're creating a global variable, and there's the tiny tiny tiny tiny possibility that it changed between declaration and usage also, you have repeated text also, innerText replaces all elements, contrary to textContent also, the else is useless and the num >= 0 is useless too
Vandana
VandanaOP4mo ago
you mean num variable?
ἔρως
ἔρως4mo ago
no, res = num creates a global variable
Vandana
VandanaOP4mo ago
but then res can be used inside the if statement only its a scoped variable.
ἔρως
ἔρως4mo ago
it's global some events can stop the execution of the code and the variable can be changed outside the function but still it's mostly repeated code
if (num < 40.000001) {
num = (num + 10).toFixed(2);
}

total.textContent = `$${res}`;
if (num < 40.000001) {
num = (num + 10).toFixed(2);
}

total.textContent = `$${res}`;
this does the same
Vandana
VandanaOP4mo ago
same as innerText right? so you mean variables declared using let inside scopes {} can become global due to some events?
ἔρως
ἔρως4mo ago
no, it isn't. innerText replaces everything with a text node, while textContent replaces just the text it's the lack of var or let or const
Vandana
VandanaOP4mo ago
so this is unavoidable right?
ἔρως
ἔρως4mo ago
it's super avoidable just add one of those and it's no longer global
Vandana
VandanaOP4mo ago
if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);---->Eq1
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}
if (num >= 0 && num <= 40) {
let res = (num + 10).toFixed(2);---->Eq1
total.innerText = `$${res}`;
} else {
res = num;
total.innerText = `$${res}`;
}
inEq1 its declared using let only
ἔρως
ἔρως4mo ago
not in the else
Vandana
VandanaOP4mo ago
so can you please tell me where innerHTML AND innerText is applicable correctly.lot many times i noticed they are not applied correctly in projects
ἔρως
ἔρως4mo ago
now-a-days, nearly nowhere innerHTML is only a "good" idea if you want to delete everything inside an element, but even then, there's better options
Vandana
VandanaOP4mo ago
okay inside the elements u mean text,else attributes etc right
ἔρως
ἔρως4mo ago
what are you talking about?
Vandana
VandanaOP4mo ago
delete everything inside an element, meaning elements text ,attributes
ἔρως
ἔρως4mo ago
nodes attributes are part of elements all elements are nodes text is also a node, but not an element and comments are nodes too, but also not elements
Jochem
Jochem4mo ago
<p id="test">Hello World.</p>
<p id="test">Hello World.</p>
doing document.getElementById('test').innerHTML = ''; would remove "Hello World." but leave the ID alone. InnerHTML only affects the contents of a node, which is the part between the > on the opening tag and the < on the ending tag.
Vandana
VandanaOP4mo ago
function calc(){
errorEle = document.querySelector('.error-msg');
let num = Number(document.querySelector('input').value);
document.querySelector('input').value = '';
let res;

if(num<0)
{
errorEle.textContent = `Error:cost cant be less than $0`;
errorEle.classList.add('error-text');
document.querySelector('.total').innerHTML = ``;
return;
}
if(num<=40)
{
errorEle.textContent = ``;
errorEle.classList.remove('error-text');
res = ((num+10).toFixed(2));----------->eq1
}

document.querySelector('.total').innerHTML = `$${res}`;

}
function calc(){
errorEle = document.querySelector('.error-msg');
let num = Number(document.querySelector('input').value);
document.querySelector('input').value = '';
let res;

if(num<0)
{
errorEle.textContent = `Error:cost cant be less than $0`;
errorEle.classList.add('error-text');
document.querySelector('.total').innerHTML = ``;
return;
}
if(num<=40)
{
errorEle.textContent = ``;
errorEle.classList.remove('error-text');
res = ((num+10).toFixed(2));----------->eq1
}

document.querySelector('.total').innerHTML = `$${res}`;

}
updated as per this.
ἔρως
ἔρως4mo ago
at least indent the code and the else is useless
Vandana
VandanaOP4mo ago
eq1 solves the pbm of inaccuracy right . Rather than regEx
ἔρως
ἔρως4mo ago
no, but it is acceptable
Vandana
VandanaOP4mo ago
that means when we are encountering decimal nos we can go ahead with toFixed(n) else multiplying the digit with 100(if we want two decimals places) then math.round then divide it back by 100.Both methods give same result which is accepatable
ἔρως
ἔρως4mo ago
there's another bug in this: it doesn't limit to 2 decimal places if the input has more than 2
Vandana
VandanaOP4mo ago
give me an example please to reproduce the issue.
ἔρως
ἔρως4mo ago
45.6789 that should bug it
Vandana
VandanaOP4mo ago
yes thanks for pointing it and go through the code.
ἔρως
ἔρως4mo ago
i will check it later
Jochem
Jochem4mo ago
I can't go through the code right now, but I will say one thing: Please format your code properly. The interpreter doesn't care about indenting, but it makes it so much harder to read. It's a little bit of extra effort, unless you use some kind of autoformatting which you can easily set up in VS Code and in Codepen, but it makes debugging and reading your code 10x easier and honestly, if I had an applicant show me their code and the indentation was all over the place, that's an instant rejection.
Want results from more Discord servers?
Add your server