views:

701

answers:

6

How to convert last 3 digits of number into 0

example 3444678 to 3444000

I can do like

(int)(3444678/1000) * 1000= 3444000

But division and multiplication could be costly...

Any other solution????

+10  A: 

You could try

n - (n % 1000)

but the modulus operator might be as costly as a division. In any case, this sounds an awful lot like a micro-optimization. Is this really your bottleneck?

Jesse Beder
No this is not any bottleneck..I was just wondering there could be some bit manipulation trick to solve this like shift operator or something else..
Alien01
This is a decent alternative, but I'm afraid it's too clever. The original way it's a little more clear what you're doing. Plus, as you said add/divide is the same cost as subtract/mod.
Bill the Lizard
I actually find it more readable than the original (since that depends on truncation of integers, which has always been non-obvious to me), but that's a matter of taste. Anyways, the original was actually multiply/divide, which *might* be a tad slower than subtract/mod.
Jesse Beder
D'oh! You're right.
Bill the Lizard
Multiply/Divide would definitely be slower than Subtract/Mod.Mod is usually implemented using the exact same circuitry (and often the exact same instruction) as division, and just using the other half of the result. Multiple is of course more complicated than subtract.
SoapBox
modulo is about 4 times faster than multiplication and about 7 times faster than division (in Java). So this should be ~28 times faster than your original implementation
Laplie
Um. If the first two things are true (and the subtraction free) then 1 modulo would be 11 times faster than one divide and one multiply, not 28. Where are those Java speeds from? It's not what I'd expect given the integer instruction speeds on common CPUs - modulo usually *is* div. What JIT?
Steve Jessop
Modulus is faster, because instead of doing div+mul, you're now just doing div. ;)
Kent Fredric
And you guys do a fantastic job of proving my point--that we programmers don't have a clue about optimizing! I just ran some really simple tests (in a loop) plus took 969ms, mult took 1156, mod took 5844! and div took 5828, so JUST DON'T OPTIMIZE! (I was surprised * performed like + and not /)
Bill K
For this quick test, I was simply doing x mod x. Turns out the CPU optimizes that. When I changed to X mod 10, times went through the roof! 3 times longer then x mod x. Always surprises me when I actually time stuff--not that it matters, had to loop to 1000000000 just to get a time I could see.
Bill K
18 nanoseconds for a div? That's extortionate! A photon could be out of my PC case, out the window, and most of the way down the front garden in that time!
Steve Jessop
(Actually, 18ns is maybe 36 cycles depending of course on your clock speed. I wonder if that matches the "official" cycle count for your CPU).
Steve Jessop
@onebyone It was in a tight loop, but there is still loop overhead to consider. A good way to figure out how long the div took might be to subtract out the time an "Add" took from the time the div took, then divide by 1,000,000,000. Add was very close to "empty loop"
Bill K
A: 
int takeAway(int num)
{
    for (int i = 1; i =< 3; i++)
    {
        num = num/10;
    }

    return num;
}

int addZeroes(int num)
{
    for (int i = 1; i =< 3; i++)
    {
        num = num*10;
    }

    return num;
}

Then you just call takeAway then addZeroes. Couldn't be simpler!

I was tempted to add in an int temp = num and then use that but figured that would be too much, even for this code.

Peter C.
this involves loop and is more costly than doing (num/1000)*1000
Alien01
Sarcasm doesn't work on the internet. Try using a smiley next time. :)
Bill the Lizard
I assumed this was a joke.
Richard A
Naturally. Given everyone had said it was fine I felt compelled to offer an "alternative". It does work, though!
Peter C.
Richard, are you implying this isn't a joke? I'm really confused now :-(
SoapBox
Now it looks like a joke
Alien01
I voted this up because it's probably the best damn solution here. Reusable and clear in intent. It's not a good sign when a programmer worries about performance of a simple division/mult! It means he doesn't get it!
Bill K
People who upvote this didn't read the question.
Andrew G. Johnson
@Andrew: ...or they just have a sense of humor?
Bill the Lizard
Or they recognize that the question is a fatally flawed attempt to insert bogus performance optimizations where they really don't belong, and think a little backlash/attention to the issue is appropriate?
Bill K
You could always use strtoint(copy(inttostr(num), 1, length(inttostr(num) - 3) + '000')). No apologies for the delphi syntax. More seriously, he could quite seriously need to optimize this, just don't do it without profiling first.
Richard A
A: 

If you want to round to an arbitrary precision and your round method lacks precision control, this should work:

In Perl at least, mod should still be faster

Heres why: Sample of ( 10 ** 7 ) * 3 random floats produced this benchmark:

Dividing
Time taken was  7 wallclock secs ( 6.83 usr  0.00 sys +  0.00 cusr  0.00 csys =  6.83 CPU) seconds

Multiplying
Time taken was  7 wallclock secs ( 6.67 usr  0.00 sys +  0.00 cusr  0.00 csys =  6.67 CPU) seconds

Modding
Time taken was  8 wallclock secs ( 7.87 usr  0.00 sys +  0.00 cusr  0.00 csys =  7.87 CPU) seconds

Multiply + Dividing
Time taken was 10 wallclock secs (10.18 usr  0.01 sys +  0.00 cusr  0.00 csys = 10.19 CPU) seconds

Do note however that 10 ** 7 * 3 is a REDICULOUS number of floats. I couldn't use 10**8 floats because for a fair test, i had to use the same float sequence for all tests, and that many floats EXHAUSTED MY 4G OF MEMORY

Ok, bit side topic :/

( Perl Implementation using only Int, which truncates instead of rounding normally )

use strict;
use warnings;
use version;
our $VERSION = qv('0.1');

sub xround { 
    my ( $number, $precision ) = @_ ;
    if ( $precision eq 0 )
    {
        return int( $number + .5 ); 
    }
    my $scale = 10 ** abs( $precision ) ;

    $number = $number / $scale if $precision > 0; 
    $number = $number * $scale if $precision < 0; 

    $number = int( $number + .5 );

    $number = $number * $scale if $precision > 0; 
    $number = $number / $scale if $precision < 0; 
    return $number;
}

my $fmt = "%s : %s  ( %s )\n";
my $n = 55555.55555;
for my $i ( -4 .. 4 )
{

    printf $fmt, $n, xround($n, $i), $i; 
}

.

55555.55555 : 55555.5556  ( -4 )
55555.55555 : 55555.556  ( -3 )
55555.55555 : 55555.56  ( -2 )
55555.55555 : 55555.6  ( -1 )
55555.55555 : 55556  ( 0 )
55555.55555 : 55560  ( 1 )
55555.55555 : 55600  ( 2 )
55555.55555 : 56000  ( 3 )
55555.55555 : 60000  ( 4 )

using Modulus method

This works as above, ( except without rounding ) using modulus method for ints, but still works for rounding into float precision ( with a bit of a slow-down for precisions right of the decimal point.

use strict;
use warnings;
use version;
our $VERSION = qv('0.1');

sub xround {
    my ( $number, $precision ) = @_;
    my $ino = int( $number );
    if ( $precision eq 0 ) {
        return $ino; 
    }
    my $power = 10**$precision;

    if ( $precision > 0 ) {
        return int( $number - ( $number % $power ) );
    }
    return $ino + int( ( $number - $ino ) / $power ) * $power;
}


my $fmt = "%s : %s  ( %s )\n";
my $n = 55555.55555;
for my $i ( -4 .. 4 )
{
    printf $fmt, $n, xround($n, $i), $i; 
}

.

55555.55555 : 55555.5555  ( -4 )
55555.55555 : 55555.555  ( -3 )
55555.55555 : 55555.55  ( -2 )
55555.55555 : 55555.5  ( -1 )
55555.55555 : 55555  ( 0 )
55555.55555 : 55550  ( 1 )
55555.55555 : 55500  ( 2 )
55555.55555 : 55000  ( 3 )
55555.55555 : 50000  ( 4 )
Kent Fredric
+1  A: 

Just as a by the way (you've gotten good input here already).

Bit manipulation never works with decimal numbers. The problem is that the values of the bits don't map to decimal at all. With BCD it works great, but nobody ever uses that (maybe BigDecimal does??? I doubt it though).

Anyway, the one base-10 trick you can use is multiplying by factors of 10, but it's never worth while unless you are coding assembly on some 1970's CPU; but just because it's polluting my memory banks, I'll post it for your amusement:

int mult10(int val) {
    int tmp_2val = val << 1; // double val
    int tmp_8val = val << 3; // 8x val
    return( tmp_2val + tmp_8val ); // 2x + 8x = 10x
}

But the math co-processor can do it so much quicker than that, just NEVER OPTIMIZE! The fact that you even take execution speed into consideration is an issue, and your "optimization" is usually as likely to slow the system down than speed it up.

I believe you can use a similar method to divide by 10, but I'm not going to try to figure it out--it's late--If I remember correctly it has something to do with examining the bits shifted out and adding values back in based on their value.

Bill K
Sometimes there are edge cases where micro-optimizations yeild benefits. Especially in some things. I saw a 3n+1 challenge where a massive speedup was added simply by doing >> 1 instead of / 2
Kent Fredric
Also, if you're using binary-coded-decimal ( BCD ) then and only then can you do bitwise math in base10 :)
Kent Fredric
I mentioned the BCD in my answer. Also, A system with a math co-processor should do pretty much any operation in one cycle--and the point is, it may be slightly faster, it may not, it NEVER matters, and if it does, testing is the only way to know.
Bill K
@Kent Fredric - if >> 1 was quicker than / 2 on a particular CPU, and the compiler didn't replace a constant power-of-two division with a shift, then I'd wonder what the compiler-writers actually *do* all day. They're supposed to think about that stuff so I don't have to ;-)
Steve Jessop
+4  A: 

A shift trick, then:

n >>= 3;
n -= n % (5 * 5 * 5);
n <<= 3;

Is it faster? Doubtful.

But here's a fun fact: gcc doesn't use division/modulus for this:

n -= n % 1000;

It multiplies by some crazy number (274877907) and does some other stuff which is presumably faster.

The moral of this story: the more obvious the purpose of your code is to the compiler, the more likely it is that the compiler will optimise it in a way you'd never think of. If the code is easier for humans to understand, that's another bonus.

Artelius
That summary/moral was perfectly phrased. I think I'll make it my tagline.
Bill K
+1  A: 

If you are working on octal, you can simply:

x &= ~0777;

If you are working on hex, you can simply:

x &= ~0xfff;

But for decimal, you should probably do it the way Jesse Beder suggests:

x -= (x % 1000);

Most systems have a fast integer divide; if you're working on a system that doesn't, you could use the double dabble algorithm to fast convert to bcd. Look at the section about dividing by ten.

Depending on what else you're trying to do, it may be easier to truncate when converting the number to a printable (string) format as Avitus suggested.

geocar