 |
|
 |
Hi, This version has special mods for my application plus a bug fix - necessary output left side '0' padding for multiple of 2 radixes. I used my IDE Embarcadero C++Builder XE Version 15.0.3953.35171 that has 64 bit unsigned longs.
Thanks,
Mark
char * lint::radix4Charset = "ADFT";
char *lint::value(int radix) {
static lint divisor, quotient, remainder, temp;
char output[ (LINT_LENGTH*32) + 1 ];
char *str_temp;
int next_char;
bool is_negative = false;
if( str_value ) {
delete [] str_value;
str_value = 0;
}
temp.copy_from(*this);
output[0] = '\0'; if( temp == 0 ) { str_value = new char[2];
str_value[0] = (radix == 62 ? radix62Charset[0] : '0');
str_value[1] = '\0';
return str_value;
}
next_char = 0; int cpl = 64 / radix;
switch( radix ) { case 2:
case 4:
case 8:
case 16:
case 32:
str_temp = new char[33];
for( int i = 0; i < LINT_LENGTH; i++ ) {
if( (next_char == 0) && (data[i] == 0) ) {
} else {
itoa((unsigned long)data[i], str_temp, radix);
int L = strlen(str_temp);
if (L<cpl && next_char != 0) {
memmove(str_temp+cpl-L,str_temp,L+1);
memset(str_temp,'0',cpl-L);
}
if (radix4Charset != (char *)Null && radix==4) for (int r4i = 0;r4i<(int)strlen(str_temp);r4i++)
str_temp[r4i]=radix4Charset[str_temp[r4i] - '0'];
strcat(output, str_temp);
next_char = strlen(output);
}
}
delete [] str_temp;
break;
default:
if( (radix == 10) && ((temp.data[0] & 0x80000000) != 0)
) {
temp.negate();
is_negative = true;
}
divisor = radix; while( temp != 0 ) { div_mod_calculate(temp, divisor, quotient, remainder);
if (radix == 62)
output[next_char++] = radix62Charset[remainder.data[LAST_DWORD]];
else
if (radix == 64)
output[next_char++] = radix64Charset[remainder.data[LAST_DWORD]];
else
if( remainder.data[LAST_DWORD] < 10 ) {
output[next_char++] = (char)('0' + remainder.data[LAST_DWORD]);
} else {
output[next_char++] = (char)(55 + remainder.data[LAST_DWORD]); }
temp = quotient; }
if( is_negative )
output[next_char++] = '-';
output[next_char++] = '\0'; strrev(output); };
str_value = new char[ strlen(output) + 1 ];
strncpy(str_value, output, strlen(output) + 1);
return str_value;
}
|
|
|
|
 |
|
 |
Hi,
Nice work with you lint class, looking forward to see an SSE/SSE2 and MMX optimised version.
I think you may have a bug in your ++ and -- operator though, the INC and DEC instructions do not affect the carry flag, so if you were to increment a value of FFFFFFFF the carry would not be affected and you would not spill over the next bit into the higher order DWORD. I haven't actually tested this with your code though.
Regards
|
|
|
|
 |
|
 |
How goes the new version?
Wonderful job on this one though, no immediate bugs noticed in the assembly.
|
|
|
|
 |
|
 |
Dear,
I notice that your assembly code does not follow the guidelines of Microsoft. It may reflect some of troubles reported in other posts.
eg.: (from MSDN Using and Preserving Registers in Inline Assembly)
When using __asm to write assembly language in C/C++ functions, you don't need to preserve the EAX, EBX, ECX, EDX, ESI, or EDI registers. For example, in the POWER2.C example in Writing Functions with Inline Assembly, the power2 function doesn't preserve the value in the EAX register. However, using these registers will affect code quality because the register allocator cannot use them to store values across __asm blocks. In addition, by using EBX, ESI or EDI in inline assembly code, you force the compiler to save and restore those registers in the function prologue and epilogue.
You should preserve other registers you use (such as DS, SS, SP, BP, and flags registers) for the scope of the __asm block. You should preserve the ESP and EBP registers unless you have some reason to change them (to switch stacks, for example). Also see Optimizing Inline Assembly.
Note If your inline assembly code changes the direction flag using the STD or CLD instructions, you must restore the flag to its original value.
|
|
|
|
 |
|
 |
I ran your test and I've got an "Access violation writing location 0xcccccdcc" under VC 2003 and same results under VC 6.0
Luc
|
|
|
|
 |
|
 |
I can also confirm crashes in VC++2003 and VC++2005. As another example, consider this simple test program, which could easily be rewritten as a unit test:
#include "lint.h"
#include < assert.h >
int main()
{
lint a;
lint b = 0L;
lint c("0"); // crash
assert(a == 0);
assert(b == 0);
assert(a == b);
assert(c == 0);
assert(a == c);
return 0;
}
The first 2 Lint objects, a and b, are constructed. However, there is a crash when trying to construct the third object, c. If we step through the code:
lint c("0");
lint::lint(const char *value) {
from_string(value);
void lint::from_string(char *value) {
static lint temp;
lint::lint() {
str_value = 0; }
The expected result is that none of the constructors should crash and that all of the assert calls should pass.
|
|
|
|
 |
|
 |
I am in the process of rewriting the class now to follow up on bugs and suggestions made by several folks.
The propblem you are having has to do with the direction flag.
As far as I know, there is no guarantee or promise made or required about saving or restoring flags when entering and exiting a function.
What this means is that I am (as far as I can tell) setting the direction flag to what I need it to be at the time I need it, but other code being generated by the compiler is making the assumption that the direction flag is already in some expected state when it needs the flag - an erroneous assumption to be sure.
In any case, to fix the problem in my next release, I intend to wrap all code that calls 'std' or 'cld' with 'pushf' and 'popf' instructions to save and restore the flag states.
Jeremy Wilson
mitselplik@cox.net
|
|
|
|
 |
|
 |
Hi,
When will you fix this? Because it can't be used in debug mode.
|
|
|
|
 |
|
 |
I started checking out your code in VC6. To make a long story short, I think your lint class could use an injection of const.
I suggest you change the member functions to the following:
lint(const lint &rvalue);
lint& operator=(const lint &rvalue);
lint& operator+=(const lint &rvalue);
friend lint& operator+(const lint& addend1, const lint& addend2);
void copy_from(const lint &rvalue);
// and so on.
For more information on const correctness, check here:
http://www.parashift.com/c++-faq-lite/const-correctness.html
|
|
|
|
 |
|
 |
working on it in my next release
Jeremy Wilson
mitselplik@cox.net
|
|
|
|
 |
|
 |
have found the bug:
for VC compiler, at start of a function, will fill the stack with 0xcccccccc, using "rep stos". and many rep stos in your coding. when you insert std, rep stosd..., you must restore direction flag using cld. in order to avoid error, you'd better insert pushfd at begin, and popfd at end in _asm{}.
again: can you finish the hex string? "abcdef0123456789"...?
for example:
void lint::copy_from(lint& rvalue) {
// here, the compile will insert code like rep stos...
long *first_addr = &data[0];
long *addr_s = &rvalue.data[0];
_asm {
pushfd; // push the flag
cld;
mov esi, addr_s;
mov edi, first_addr;
mov ecx, LINT_LENGTH;
rep movsd;
popfd;// pop the flag
}
}
Thanks.
|
|
|
|
 |
|
 |
So what you are really saying is that the compiler has a bug in that it produces code that assumes the direction flag hasn't been messed with when it creates a rep stos instruction? Or am I not setting the direction properly when I issue a rep stosd command?
Pushing and popping the flags because of someone else's incorrect coding is not my idea of efficiency. So, I will look into adding code wrapped in #ifdef...#endif tags that look specifically for VC6 in my next release.
And yes, I'll work on the hex string for my next release...
Jeremy Wilson
mitselplik@cox.net
|
|
|
|
 |
|
 |
I don't think this is a compiler bug; generally APIs and applications are written with the idea that the direction flag is cleared and thus a REP will move forward in memory, not backward. So if you use STD to set the direction to go backward you should reset to move forward or you may encounter several bugs. If you look at the Windows API even you will find they do not touch the direction flag, so what he's saying is that if you reverse it you better clear it afterwards. You do not need to use "CLD" everywhere, you should instead assume CLD and only use CLD after you have used STD.
So:
REP MOVSD is fine
but
STD
REP MOVSD
CLD
needs to be followed by a clear. Also, he is talking about building debug version, this is the time when the generated VC++ code will fill memory with INT 3's.
8bc7c0ec02c0e404c0cc0680f7018827ebee
|
|
|
|
 |
|
 |
Since it hasn't been done yet by the author, I went though my copy of the code and stuck the pushfd and popfd statements into every _asm{} block as per this suggestion to see if it works.
It appears to do the trick. There's quite a few of the _asm{} blocks to do this to, but seems to fix that error trying to write to invalid memory, at least so far.
Good catch, kevincpp! And nice lib, Jeremy! Thanks both of you. This is handy.
|
|
|
|
 |
|
 |
Hi,
Can you give me a copy of the version you fixed with pushs and pops.
Thanks alot for time, Bye.
|
|
|
|
 |
|
|
 |
|
 |
1) I agree with the name change suggestion, in the comment below
2) why not make your class a template with LINT_LENGTH as a parameter?
That way no editing of your file is needed, and multiple lengths can
be used in the same project.
3) Did you consider adding support for fixed-place decimals?
I've always wanted something in C++ that gave about the same functionality
as the .NET Decimal type. That would be cool
Just my 2 cents worth...
Warren
|
|
|
|
 |
|
 |
Until today, I had this same article posted under unedited contributions. While there I got a lot of suggestions that I plan to implement in the next release all but number 3 was on my list
Jeremy Wilson
mitselplik@cox.net
|
|
|
|
 |
|
 |
Lint is already in used ( and well known ) : http://www.gimpel.com/
Maximilien Lincourt
Your Head A Splode - Strong Bad
|
|
|
|
 |
|
 |
Perhaps to Linteger?
|
|
|
|
 |
|
 |
or LNL (Lint's not Lint) ?!?
|
|
|
|
 |
|
 |
Warren D Stevens wrote: or LNL (Lint's not Lint)
Almost! How about QNL (QNL is Not Lint)
I'm assuming you are parodying GNU...
|
|
|
|
 |
|
 |
Harold Bamford wrote: I'm assuming you are parodying GNU...
Yes, that was the plan (But my humour often goes into the sarchasm)[^]
Warren
|
|
|
|
 |
|
 |
Until today, I had this same article posted under unedited contributions. While there I got a lot of suggestions that I plan to implement in the next release.
I was thinking to change the name to one of the following (in order of my preference:
1. LLint (for Large Long Integer)
2. Fermat (lets face it...he was an awesome mathematician and needs a computer thingy named after him)
3. Linteger (another user's suggestion, but I prefer concise names personally
4. Taban (Thats a Big arse number!)
I'll take other suggestions for a name and votes on the above.
Jeremy Wilson
mitselplik@cox.net
|
|
|
|
 |
|
 |
I vote for Linteger. It's better readable in code and I think the name should say what it is.
BR
Stephan
|
|
|
|
 |