When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
55 messages · Page 1 of 1 (latest)
When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
floor(log10(num)) + 1
The secret agent Programín has to send a coded message and he doesn't know how to do it. writes
a recursive module called ej3 to help you. This module must receive a number and
returns encoded, this module should not print anything on the screen. To encode the number
The procedure is as follows, the number is decomposed into figures and if the figure is a zero, it is
becomes 1, if it is a multiple of 3 its value is multiplied by 2 and in any other case it is left
how are you doing. The coded number is the result of multiplying all the figures processed as
has been indicated.
Examples:
Number: 2345789 Encoded: 241920 (obtained by 232457892)
Number: 103468 Encoded: 2304 (obtained through 1132462*8)
this is the actual problem
how does this work
the base-10 logarithm of a number tells you what power you need to raise 10 to to get the number. e.g. for 1000, that's 3
then you add 1 to that because 2.g. 256 will only give you 2.4
The best (and probably) fastest way is to loop dviding the number by 10 until it reaches 0;
int numlength(long l) {
int length = 0;
for (; l != 0; l /= 10)
++length;
return count;
}
that's why you add 1
but doing 10^2 is 100 and log10(100)+1 = 3
and 100 takes 3 digits to write
Maybe the logorithm trick might be faster, but I have doubts about the performance of the log method
why is that?
it's potentially a trickier math operation for the CPU to do
sooo?
calculating the log10 may be slower than this divide-and-test method
okay
;asm -O2
#include <math.h>
int numlength(long l) {
if (l == 0)
return 1;
int length = 0;
for (; l != 0; l /= 10)
++length;
return length;
}
int numlength2(long l) {
return (int)log10(l) + 1;
}
numlength:
mov ecx, 1
test rdi, rdi
je .L1
movabs rsi, 7378697629483820647
xor ecx, ecx
.L3:
mov rax, rdi
add ecx, 1
imul rsi
mov rax, rdi
sar rax, 63
sar rdx, 2
mov rdi, rdx
sub rdi, rax
jne .L3
.L1:
mov eax, ecx
ret
numlength2:
pxor xmm0, xmm0
sub rsp, 8
cvtsi2sd xmm0, rdi
call log10
add rsp, 8
cvttsd2si eax, xmm0
add eax, 1
ret
wtf is this
it's the assembly that the code they posted compiles into
lol
Ya just ignore it, it is just a curiosity thing on my part
Anyway, those are two function that can give you the length
rust cause it's easier to benchmark, so it's only ballpark, but didn't expect this much of a difference
log time: [1.6602 ns 1.6645 ns 1.6692 ns]
div time: [4.4385 ns 4.4506 ns 4.4631 ns]
What sizes of numbers did you test with?
I imagine the numbers approaching LONG_MAX, might be longer, but number closer to 0 are shorter :P
that was 5 digit, currently rewriting to do 1, 12, 123, 1234, ... 123456789
this benchmark library generates some nice graphs I'm excited to see
div/1 time: [1.1302 ns 1.1339 ns 1.1380 ns]
div/12 time: [1.6318 ns 1.6361 ns 1.6403 ns]
div/123 time: [2.2935 ns 2.3000 ns 2.3067 ns]
div/1234 time: [3.1804 ns 3.1881 ns 3.1956 ns]
div/12345 time: [4.0195 ns 4.0308 ns 4.0424 ns]
div/123456 time: [5.1414 ns 5.1911 ns 5.2565 ns]
div/1234567 time: [6.4852 ns 6.5035 ns 6.5222 ns]
div/123456789 time: [8.8588 ns 8.8939 ns 8.9316 ns]
log/1 time: [1.5009 ns 1.5389 ns 1.5684 ns]
log/12 time: [1.6562 ns 1.7749 ns 1.9179 ns]
log/123 time: [1.5343 ns 1.5391 ns 1.5441 ns]
log/1234 time: [1.5270 ns 1.5315 ns 1.5359 ns]
log/12345 time: [1.5286 ns 1.5348 ns 1.5425 ns]
log/123456 time: [1.6939 ns 1.7347 ns 1.7715 ns]
log/1234567 time: [1.5365 ns 1.5420 ns 1.5484 ns]
log/123456789 time: [1.5370 ns 1.5421 ns 1.5477 ns]
on my system at least, seems there's no reason to not use log
You might get different results if you use ffast-math
I'm not sure about that
fast-math lets floating-point get a bit wonky (iirc to save time on checking edge cases), but the div method isn't using any floating points
Oh I didn't see it was integer division, sorry
here's the actual code I benched:
fn len_div(mut l: i32) -> u32 {
if l == 0 {
return 1;
}
let mut len = 0;
while l != 0 {
len += 1;
l /= 10;
}
len
}
fn len_log(l: i32) -> u32 {
l.ilog10() + 1
}
It makes sense that logarithm isn't slower than repeated division, since the logarithm is probably just a taylor series under the hood
You can also use int snprintf(char * s, size_t n, const char * format, ...) for this where n=0 and s= NULL
It returns the number of characters it would write if it had enough space
So, snprintf(NULL, 0, "%d", 37473) would return 5 for 5 digits
That's probably might be safer since you aren't relying on math and it's just directly telling you how many characters it's gonna write.
Okay so my dumb ass forgot the printf's return the number of characters printed
I was thinking of sprintf, but thought the requirement of needing a large enough buffer was putting the cart before the horse in a sense
Also didn't know that it still counts beyond the n-bound
Oh it does
TIL