Math.floor VS Math.trunc JavaScript
Asked Answered
A

3

86

Background

I am making a function that receives a positive number and then rounds the number to the closest integer bellow it.

I have been using Math.floor, but recently I discovered Math.trunc.

I am aware that both will return the same value, given a positive number, and that they work in completely different ways. I am interested in exploring this behavior.

Questions

  1. Which one is faster ?
  2. Which one should I use?
Aetiology answered 1/8, 2016 at 15:31 Comment(7)
It is supported in all browsers, I am aware of that. I just wonder how much impact using each one has. That's why I created this questions, but the community seems to not like it :SAetiology
If by “it” you mean Math.trunc, you’d be wrong. Math.trunc is very new and not supported in every browser.Corolla
@Xufox: You are correct. I discarded IE from my evaluation, but then again I don't really care about it, nor do i think anyone in their perfect sense would :pAetiology
Thank you very much. I feel the same! Thank you for the kindness!Aetiology
I made a quick performance test, and the difference per operation on my computer is about 0.00001 milliseconds. In other words, the difference is almost always practically meaningless.Fencible
@Juhana would be nice if we could have that as an answer !Aetiology
Here's an example #66329006Extenuation
L
144

Actually, there is much more alternative ways to remove the decimals from a number. But it's a tradeoff of readability and speed.

Choosing the right one depends on what you need. If you just need to just remove decimals, always use trunc() or bitwise operators.
The floor(), ceil() and round() are conceptually very different from trunc().

Math library

You already know these. Always use them in a standard, non-critical code.

var v = 3.14; [Math.trunc(v), Math.round(v), Math.floor(v), Math.ceil(v)]
// prints results

for different input values you get these results

 v        t   r   f   c
 3.87 : [ 3,  4,  3,  4]
 3.14 : [ 3,  3,  3,  4]
-3.14 : [-3, -3, -4, -3]
-3.87 : [-3, -4, -4, -3]

Math.trunc() cuts away (truncates) the decimal places.
Math.round() rounds towards closest integer number.
Math.floor() rounds towards closest lower integer number. 3.5 -> 3 -3.5 -> -4
Math.ceil() rounds towards closest higher integer number. 3.5 -> 4 -3.5 -> -3


But this is more fun :)

Binary operations and bitwise operators

If you look at them in the code, it might not be apparent from the first glance what they do, so don't use them in normal code. Though in some cases, they might be useful. For example calculating coordinates in a <canvas/>. They are much faster, but come with limitations.

Conceptually, they work this way:

  • The operands are converted to 32-bit signed integers and thus lose all decimal fractions.

ATTENTION:
Numbers with more than 32 bits get their most significant (leftmost) bits discarded and the leftmost bit becomes the new sign bit.

[
  0b011100110111110100000000000000110000000000001, //  15872588537857
~~0b011100110111110100000000000000110000000000001, // -1610588159
             ~~0b10100000000000000110000000000001, // -1610588159
]

Bitwise logical operators

  • Each bit in the first operand is paired with the corresponding bit in the second operand. (First bit to first bit, second bit to second bit, and so on.)
  • The operator is applied to each pair of bits, and the result is constructed bitwise.

Bitwise shift operators

  • These operators take a value to be shifted and a number of bit positions to shift the value by.

truncating

However, when truncating, we always use a 0, zero, a false as a second operand, that doesn't do anything to the original value, except for converting to integer, in these cases:

~    NOT    ~~v

|    OR    v | 0

<<   Left shift    v << 0

>>   Signed right shift    v >> 0

>>>  Zero-fill right shift    v >>> 0

var v = 3.78;
[ ~~v ,  v | 0 ,  v << 0 ,  v >> 0 ,  v >>> 0 ]
// prints these results

 3.78 : [ 3,  3,  3,  3, 3]
 3.14 : [ 3,  3,  3,  3, 3]
-3.74 : [-3, -3, -3, -3, 4294967293]
-3.14 : [-3, -3, -3, -3, 4294967293]

Performance

https://jsperf.com/number-truncating-methods/1

enter image description here

Lenorelenox answered 2/8, 2016 at 7:41 Comment(3)
Very bad idea. Large numbers do not go through bitwise operators the way you think. Try (Date.now()+0.5)>>>0. Doesn't give you the right answer. Do not use bitwise operators for flooring a value unless you are doing performance critical work and you know the numbers will always be small. Graphics work would be an example.Careen
@JasonMitchell That was actually written in the answer, but I have highlighted it in case more people miss that like you did. Thanks for the hint though.Lenorelenox
In short: use trunc ;)Manque
H
25

The existing answers have explained well about the performance. However, I could not understand the functional difference between Math.trunc and Math.floor from either the question or the answers and hence I have put my finding in this answer.

Math.trunc rounds down a number to an integer towards 0 while Math.floor rounds down a number to an integer towards -Infinity. As illustrated with the following number line, the direction will be the same for a positive number while for a negative number, the directions will be the opposite.

trunc: towards 0    
floor: towards -Infinity


                   -3      -2     -1      0      1      2      3
-Infinity ... ------+----|--+------+------+------+------+--|----+------ .... Infinity
                         b                                 a    

Demo:

var a = 2.3, b = -2.3;
console.log("\t\t\t" + a + "\t\t" + b + "\r\n" + "Math.trunc: " + Math.trunc(a) + "\t\t" + Math.trunc(b) + "\r\n" + "Math.floor: " + Math.floor(a) + "\t\t" + Math.floor(b));

Output:

            2.3     -2.3
Math.trunc: 2       -2
Math.floor: 2       -3
Hubris answered 16/3, 2021 at 17:23 Comment(0)
P
12

if the argument is a positive number, Math.trunc() is equivalent to Math.floor(), otherwise Math.trunc() is equivalent to Math.ceil().

for the performance check this one and the fastest one is Math.trunc

var t0 = performance.now();
var result = Math.floor(3.5);
var t1 = performance.now();
console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);
var t0 = performance.now();
var result = Math.trunc(3.5);
var t1 = performance.now();
console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);

the result is Took 0.0300 milliseconds to generate: 3 Took 0.0200 milliseconds to generate: 3

so if the arguments are only positive numbers you can use the fastest one.

Pigeontoed answered 1/8, 2016 at 15:50 Comment(4)
@StevenHansen Looks like a fun challenge to me! How much would you say to be a reasonable value ? 1 million? perhaps 2?Aetiology
The benchmarking here only complicates the answer, which itself is perfectly simple.Fled
Definitely should be the selected answer for this question!Natalienatalina
performing a benchmark on one operation is not enough to prove your point. You have to generate an array of at least a thousand floats to parse then loop on this array with both floor and trunc to compare. like this: replit.com/@Freezystem/TruncOrFloor#index.jsAlisealisen

© 2022 - 2024 — McMap. All rights reserved.