|
|
View previous topic :: View next topic |
Author |
Message |
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
PIC24EP unsigned int64 calculations [SOLVED] |
Posted: Wed Oct 14, 2020 2:02 pm |
|
|
Compiler: 5.026
Device: PIC24EP512GP806
Hi again,
To make a long story short, I am trying to do a calculation using unsigned int64 values. The result is plain wrong and I'm going nuts. The values indicated below come from an SD card driver. I use certain values to calculate the card size and the amount of space left when my MCU boots.
Important note: This exact same code returns the expected numbers using a 2GB and a 4GB SD card. The problem is with a 32GB SD card. I don't have an 8 nor a 16 to test.
unsigned int64 Val1 = 32768;
unsigned int64 Val2 = 976991;
unsigned int64 Val3 = Val1 * Val2;
Do this on a calculator --> 32768 * 976991 = 32,014,041,088 <----- This is the correct answer.
But this same calculation on the MCU returns 1,949,270,016.
So here's the original source code followed by the output on the screen:
Screen printed values returned by the SD card driver:
fs->fs_type = 3
fs->sects_clust = 64
fs->sects_fat = 7633
fs->max_clust = 976994
Code:
unsigned int64 Val1 = 512 * fs->sects_clust;
unsigned int64 Val2 = fs->max_clust - fs->fs_type;
unsigned int64 Val3 = Val1 * Val2;
fprintf( MONITOR_SERIAL, "\n\r\n\r1) Val1 = 512 * fs->sects_clust ------------> 512 * %d = %Lu", fs->sects_clust, 512 * fs->sects_clust );
fprintf( MONITOR_SERIAL, "\n\r2) Val2 = fs->max_clust - fs->fs_type -----> %Ld - %d = %Lu", fs->max_clust, fs->fs_type, fs->max_clust - fs->fs_type );
fprintf( MONITOR_SERIAL, "\n\r3) Val3 = Val1 * Val2 ------------------------> %Lu * %Lu = %Lu", 512 * fs->sects_clust, fs->max_clust - fs->fs_type, (512 * fs->sects_clust) * (fs->max_clust - fs->fs_type ));
fprintf( MONITOR_SERIAL, "\n\r4) Val3 = Val1 * Val2 ------------------------> %Lu * %Lu = %Lu", Val1, Val2, Val1 * Val2);
SDCard.Size = 512 * fs->sects_clust * (fs->max_clust - fs->fs_type);
fprintf( MONITOR_SERIAL, "\n\rCard size: %Ld", SDCard.Size );
Screen output:
1) Val1 = 512 * fs->sects_clust ------------> 512 * 64 = 32768
2) Val2 = fs->max_clust - fs->fs_type -----> 976994 - 3 = 976991
3) Val3 = Val1 * Val2 ------------------------> 32768 * 976991 = 1949270016
4) Val3 = Val1 * Val2 ------------------------> 42949705728 * 976991 = 41961475948904448
Card size: 1949270016
This code returns the proper numbers when I insert a 2GB and 4GB. Also note the two Val3 calculations: both use Val1 but the first one uses the calculation '32768 * 976991' and the other uses the direct 'Val1' variable. Both return different results.
I'm confused.
Thanks!
Ben
[EDIT] All I want, honestly, is to be able to make a calculation like 32768 * 976991 and get the correct answer printed and in the variable. The answer to this calculation should be 32,014,041,088.
Last edited by benoitstjean on Thu Oct 15, 2020 6:57 am; edited 1 time in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Wed Oct 14, 2020 2:22 pm |
|
|
It 'might ' be a compiler version problem... Heck I can't do int64 ! got an old compiler.... I'll bet Mr. T. KNOWS the answers though !! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Oct 14, 2020 2:47 pm |
|
|
976991 * 32768 = 0x7 742F 8000
Take off the leading '7' and you get:
0x742F8000 = 1,949,270,016
So it's truncating anything above 32 bits.
The current CCS manual says this in the printf section:
Quote: | Longs in the printf may be 16 or 32 bit. |
It doesn't say anything about printing 64-bit numbers. |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Oct 14, 2020 3:05 pm |
|
|
Oh, smart man!
So then, is there a way to print 64 bit numbers? I mean, the compiler supports int64 numbers, printing a 64-bit number would be nice...
Any idea how to approach this?
Ben
[EDIT] I guess since the smallest card logically that should go in there is at least a 2GB, even if it was 1GB, I could always just divide whatever result by 10 so be able to print somewhat of the correct value, but the real value is whatever got calculated.... but any suggestion on how to print the 64 bit value is welcome! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Oct 14, 2020 3:33 pm |
|
|
You could print the two halves of it in hex:
Code: |
unsigned int64 temp = 32014041088;
printf("%lx %lx \r", temp>>32, temp & 0xFFFFFFFF); |
|
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Oct 14, 2020 7:43 pm |
|
|
Thanks PCM programmer.
The problem is that printing in hex will be no good for my users as they don't understand hex. Back to your code, check it out with the very odd results. Code related to your suggestion is in blue:
Code:
unsigned int64 Val3 = (512 * fs->sects_clust) * (fs->max_clust - fs->fs_type );
unsigned int64 temp = 32014041088;
fprintf( MONITOR_SERIAL, "\n\rVal3 = %Ld %Ld", Val3>>32, Val3&0xFFFFFFFF );
fprintf( MONITOR_SERIAL, "\n\rVal3 = %lx %lx", Val3>>32, Val3&0xFFFFFFFF );
fprintf( MONITOR_SERIAL, "\n\rTemp = %Ld %Ld", temp>>32, temp&0xFFFFFFFF );
fprintf( MONITOR_SERIAL, "\n\rTemp = %lx %lx", temp>>32, temp&0xFFFFFFFF );
Output:
Val3 = 0 1949270016
Val3 = 0 742f8000
Temp = 7 1949270016
Temp = 7 742f8000
Now the question is why is the second part the same for both Val3 and Temp but the first part is not?? I mean, since both contain the same 'second part', it points to me that both Val3 and Temp are equal to 32014041088. I can easily calculate Val3 using the following values returned by the SD driver:
fs->fs_type: 3
fs->sects_clust: 64
fs->max_clust: 976994
unsigned int64 Val3 = (512 * fs->sects_clust) * (fs->max_clust - fs->fs_type );
512 * 64 * (976994 - 3 ) = 32768 * 976,991 = 32014041088
So both Val3 and Temp are the same value but yield different results? I'm at a loss here.
All I want is print the available size for the user to see but also so that the system knows what size of card it is using thus be able to calculate how much free space it has. Then when the space is running low, a message is sent-out to a user.
Thanks again.
Benoit |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Wed Oct 14, 2020 8:04 pm |
|
|
Cheat. Instead of printing the actual size, calculate the % free space and print that.
Code: | Capacity: 32GB, 43% free. |
or:
Code: | Capacity: 8GB, <1% free. |
You get the idea. So what if you can't print out, to the nearest byte, how much space remains. Numbers that large are meaningless to most people anyway. Or be creative: divide the real space remaining by 100. Then an int32 can account for up to ~400GB. So what if the last two digits are always "00"? Close enough. Work around the limits of the tools available to you. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Wed Oct 14, 2020 8:22 pm |
|
|
According to the compiler manual, the itoa() function supports 64bit numbers. Have you tried using that to make a string, and then pass that into printf using %s?
Also note that the compiler manual states that 64bit numbers can only be signed, not unsigned. Even if you have code using unsigned 64bits that works, there is no guarantee it will work at edge cases or in the next compiler release.
itoa():
Code: |
itoa( )
Syntax:
string = itoa(i32value, i8base, string)
[PCD] string = itoa(i48value, i8base, string)
[PCD] string = itoa(i64value, i8base, string)
Parameters:
i32value is a 32 bit int
[PCD] i48value is a 48 bit int
[PCD] i64value is a 64 bit int
i8base is a 8 bit int
string is a pointer to a null terminated string of characters
Returns:
string is a pointer to a null terminated string of characters
|
Basic Types:
Quote: |
Note: All types, default are unsigned. [PCD] All types, except float char, by default are signed. However, may be preceded by unsigned or signed (Except int64 may only be signed) . Short and long may have the keyword INT following them with no effect. Also see #TYPE to change the default size.
|
|
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Oct 14, 2020 8:43 pm |
|
|
Hi Newguy (makes me laugh, you've been around quite some time! Not new anymore!),
The biggest SD card that I can handle is 32GB. This equates to let's say 32,014,041,088 bytes depending on how it's formatted.
But I'm just wondering if the MCU can actually handle this big of a number. It's a 35-bit number.
This works:
[EDIT] Using a 4GB card for the example below.
float Percentage = ((float) SDCard.SpaceAvailable / (float) SDCard.Size ) * 100;
fprintf( MONITOR_SERIAL, "\n\r Available space: %3.1f %%", Percentage);
It displays:
Available space: 84.7 %
I guess I could live with this but I'd still like to know why I can't work with the 35-bit value of 32,014,041,088.
Off to bed, thanks to all, will attack this tomorrow morning.
Ben
Last edited by benoitstjean on Wed Oct 14, 2020 8:46 pm; edited 1 time in total |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Oct 14, 2020 8:45 pm |
|
|
Hi Jeremiah,
Good point, I will try that tomorrow as well. Maybe if I can get the percentage working on the 32GB card (my last post was using a 4GB card), then I guess I could also play with the itoa function.
Good night and thank you for your time.
Ben |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Oct 14, 2020 8:55 pm |
|
|
Tried with the 32GB card and it doesn't like my int64 values:
int64 Size;
int64 SpaceAvailable;
I've put two approximate 530MB files on the card.
Total size: 1949270016 bytes (fprintf of Size variable)
Free space: 866319036 bytes (fprintf of SpaceAvailable variable)
Available space: 44.4 %
The calculation of 44.4% is fine using the numbers above but these are based on the values shown here which is a truncated version of the real number. They are int64, not unsigned int64.
So (866319036 / 1949270016 ) * 100 is in fact 44.4%..... but the card size is 32GB and the space available is around 31GB so 1/32 of the card is used.... not 44%.
Will investigate more tomorrow.
Ok, good night for real.
Ben |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Thu Oct 15, 2020 2:16 am |
|
|
itoa, does work. I have used it for int64's.
In your calculation, the 'total size' printout, is losing the '7' digit off the
front, so the total size is actually 0x700000000 larger than the printed
value.
1949270016 + 0x700000000 = 32014041088 (correct 32GB)
The percentage used, is then:
866319036/32014041088 *100 = 2.7%
Worth possibly just being 'aware', that the code I posted the other day
for adding commas to printed values, will work with an int64 value. You
could just omit the part that adds the commas if you don't want these.... |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Thu Oct 15, 2020 6:12 am |
|
|
Hi Ttelmah,
Thanks for the details. That makes sense but tell me what is wrong here because it's still not displaying the correct value:
I didn't post the types for these variables (which are part of some other structure):
int8 fs->sects_clust
int32 fs->max_clust
int8 fs->fs_type
Code:
unsigned int64 temp = (512 * fs->sects_clust) * (fs->max_clust - fs->fs_type );
unsigned char Size[15] = "";
itoa( temp, 10, Size );
fprintf( MONITOR_SERIAL, "\n\r1) itoa = %s", Size );
fprintf( MONITOR_SERIAL, "\n\r2) Temp = %Ld", temp & 0x700000000 + temp & 0xFFFFFFFF );
Output:
1) itoa = 1949270016
2) Temp = 1949237248
So something is not liking over 32-bit numbers.... or else, what am I doing wrong? How can I display the darn number?
Ben |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Thu Oct 15, 2020 6:44 am |
|
|
'Classic'. Think about it.
fs->sects_clust is an int8.
512 is an int16.
So this multiplication is done using int16 maths and gives an int16 result.
You then multiply this by a value that is an int32. So int32 arithmetic will
be used.....
You need:
unsigned int64 temp = (512 * fs->sects_clust) * (int64)(fs->max_clust - fs->fs_type );
Note the cast, which forces this value to be converted up to an int64.
Result int64 maths is then used....
Maths in C is always done using the 'higher' type of the values passed to
the operator. The size of the result has no effect on the maths size used.
Now some C's on things like the PC, implicitly use the floating point
coprocessor, and this does automatically convert values upwards. C's
on other platforms do not. |
|
|
benoitstjean
Joined: 30 Oct 2007 Posts: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Thu Oct 15, 2020 6:56 am |
|
|
Again, you saved the day.
I tried typecasting yesterday but I guess I didn't typecast to the correct type!
Total size: 32014041088 bytes
Free space: 30925863160 bytes
Available space: 96.6 %
Works!
Bravo! And thanks for all the others who picthed-in!
Ben |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|