From 279abb1b37a67aa818e3b43447eac047031ca37f Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Thu, 27 Sep 2001 07:55:36 +0000 Subject: [PATCH] Implement floating point size conversions (with help from BitVector). Interface changed slightly (only affects symrec.c right now). Tests committed to check a single case (pi) with all three size conversion functions. Still need to write to/from ASCII string functions (the hard part). svn path=/trunk/yasm/; revision=247 --- libyasm/floatnum.c | 230 +++++++++++++++++++++++++++++++--- libyasm/floatnum.h | 36 +++++- libyasm/symrec.c | 4 +- libyasm/tests/Makefile.am | 9 +- libyasm/tests/floatnum_test.c | 122 ++++++++++++++++++ src/floatnum.c | 230 +++++++++++++++++++++++++++++++--- src/floatnum.h | 36 +++++- src/symrec.c | 4 +- src/tests/Makefile.am | 9 +- src/tests/floatnum_test.c | 122 ++++++++++++++++++ 10 files changed, 756 insertions(+), 46 deletions(-) create mode 100644 libyasm/tests/floatnum_test.c create mode 100644 src/tests/floatnum_test.c diff --git a/libyasm/floatnum.c b/libyasm/floatnum.c index bef9080e..50656aa6 100644 --- a/libyasm/floatnum.c +++ b/libyasm/floatnum.c @@ -3,6 +3,8 @@ * * Copyright (C) 2001 Peter Johnson * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * * This file is part of YASM. * * YASM is free software; you can redistribute it and/or modify @@ -29,8 +31,20 @@ #ifdef STDC_HEADERS # include +# include +#endif + +#include +#define _(String) gettext(String) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) #endif +#include "bitvect.h" +#include "file.h" + #include "errwarn.h" #include "floatnum.h" @@ -42,36 +56,224 @@ floatnum_new(char *str) floatnum *flt = malloc(sizeof(floatnum)); if (!flt) Fatal(FATAL_NOMEM); - /* TODO */ + + flt->mantissa = BitVector_Create(64, TRUE); + if (!flt->mantissa) + Fatal(FATAL_NOMEM); + + /* check for + or - character and skip */ + if (*str == '-') { + flt->sign = 1; + str++; + } else if (*str == '+') { + flt->sign = 0; + str++; + } else + flt->sign = 0; + return flt; } -unsigned long -floatnum_get_int(floatnum *flt) +int +floatnum_get_int(unsigned long *ret_val, const floatnum *flt) { - return 0; /* TODO */ + unsigned char t[4]; + + if (floatnum_get_single(t, flt)) + return 1; + + LOAD_LONG(*ret_val, &t[0]); + return 0; } -unsigned char * -floatnum_get_single(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "single precision" format: + * 32 bits: + * Bit 31 Bit 22 Bit 0 + * | | | + * seeeeeee emmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 127 exponent + * s = sign bit + * m = mantissa bits, bit 23 is an implied one bit. + */ +int +floatnum_get_single(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned long exponent = flt->exponent; + unsigned int *output; + unsigned char *buf; + unsigned int len; + + output = BitVector_Create(32, TRUE); + if (!output) + Fatal(FATAL_NOMEM); + + /* copy mantissa */ + BitVector_Interval_Copy(output, flt->mantissa, 0, 40, 23); + + /* round mantissa */ + BitVector_increment(output); + + if (BitVector_bit_test(output, 23)) { + /* overflowed, so divide mantissa by 2 (shift right) */ + BitVector_shift_right(output, 0); + /* and up the exponent (checking for overflow) */ + if (exponent+1 >= 0x10000) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + exponent++; + } + + /* adjust the exponent to bias 127 */ + exponent -= 32767-127; + if (exponent >= 256) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + + /* move exponent into place */ + BitVector_Chunk_Store(output, 8, 23, exponent); + + /* merge in sign bit */ + BitVector_Bit_Copy(output, 31, flt->sign); + + /* get little-endian bytes */ + buf = BitVector_Block_Read(output, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 4) + InternalError(__LINE__, __FILE__, + _("Length of 32-bit BitVector not 4")); + + /* copy to output */ + memcpy(ptr, buf, 4*sizeof(unsigned char)); + + /* free allocated resources */ + free(buf); + + BitVector_Destroy(output); + + return 0; } -unsigned char * -floatnum_get_double(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "double precision" format: + * 64 bits: + * bit 63 bit 51 bit 0 + * | | | + * seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 1023 exponent. + * s = sign bit. + * m = mantissa bits. Bit 52 is an implied one bit. + */ +int +floatnum_get_double(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned long exponent = flt->exponent; + unsigned int *output; + unsigned char *buf; + unsigned int len; + + output = BitVector_Create(64, TRUE); + if (!output) + Fatal(FATAL_NOMEM); + + /* copy mantissa */ + BitVector_Interval_Copy(output, flt->mantissa, 0, 11, 52); + + /* round mantissa */ + BitVector_increment(output); + + if (BitVector_bit_test(output, 52)) { + /* overflowed, so divide mantissa by 2 (shift right) */ + BitVector_shift_right(output, 0); + /* and up the exponent (checking for overflow) */ + if (exponent+1 >= 0x10000) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + exponent++; + } + + /* adjust the exponent to bias 1023 */ + exponent -= 32767-1023; + if (exponent >= 2048) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + + /* move exponent into place */ + BitVector_Chunk_Store(output, 11, 52, exponent); + + /* merge in sign bit */ + BitVector_Bit_Copy(output, 63, flt->sign); + + /* get little-endian bytes */ + buf = BitVector_Block_Read(output, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 8) + InternalError(__LINE__, __FILE__, + _("Length of 64-bit BitVector not 8")); + + /* copy to output */ + memcpy(ptr, buf, 8*sizeof(unsigned char)); + + /* free allocated resources */ + free(buf); + + BitVector_Destroy(output); + + return 0; } -unsigned char * -floatnum_get_extended(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "extended precision" format: + * 80 bits: + * bit 79 bit 63 bit 0 + * | | | + * seeeeeee eeeeeeee mmmmmmmm m...m m...m m...m m...m m...m + * + * e = bias 16384 exponent + * m = 64 bit mantissa with NO implied bit! + * s = sign (for mantissa) + */ +int +floatnum_get_extended(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned short exponent = flt->exponent; + unsigned char *buf; + unsigned int len; + + /* Adjust the exponent to bias 16384 */ + exponent -= 0x4000; + if (exponent >= 0x8000) + return 1; /* exponent too large */ + + /* get little-endian bytes */ + buf = BitVector_Block_Read(flt->mantissa, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 8) + InternalError(__LINE__, __FILE__, + _("Length of 64-bit BitVector not 8")); + + /* copy to output */ + memcpy(ptr, buf, 8*sizeof(unsigned char)); + + /* Save exponent and sign in proper location */ + SAVE_SHORT(&ptr[8], exponent); + if (flt->sign) + ptr[9] |= 0x80; + + /* free allocated resources */ + free(buf); + + return 0; } void -floatnum_print(floatnum *flt) +floatnum_print(const floatnum *flt) { /* TODO */ } diff --git a/libyasm/floatnum.h b/libyasm/floatnum.h index bbea9755..8ac8bf6f 100644 --- a/libyasm/floatnum.h +++ b/libyasm/floatnum.h @@ -3,6 +3,8 @@ * * Copyright (C) 2001 Peter Johnson * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * * This file is part of YASM. * * YASM is free software; you can redistribute it and/or modify @@ -22,16 +24,38 @@ #ifndef YASM_FLOATNUM_H #define YASM_FLOATNUM_H +/* 88-bit internal floating point format: + * xxxxxxxs eeeeeeee eeeeeeee m..m m..m m..m m..m m..m m..m m..m m..m + * Sign exponent mantissa (64 bits) + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0| + * 63 55 47 39 31 23 15 7 0 + * + * Only L.O. bit of Sign byte is significant. The rest is garbage. + * Exponent is bias 32767 exponent. + * Mantissa does NOT have an implied one bit (it's explicit). + */ typedef struct floatnum_s { - unsigned char extval[10]; /* float stored in extended precision */ + unsigned int *mantissa; /* Allocated to 64 bits */ + unsigned short exponent; + unsigned char sign; } floatnum; floatnum *floatnum_new(char *str); -unsigned long floatnum_get_int(floatnum *flt); -unsigned char *floatnum_get_single(unsigned char *ptr, floatnum *flt); -unsigned char *floatnum_get_double(unsigned char *ptr, floatnum *flt); -unsigned char *floatnum_get_extended(unsigned char *ptr, floatnum *flt); -void floatnum_print(floatnum *flt); +/* The get functions return nonzero if flt can't fit into that size format. */ + +/* Essentially a convert to single-precision and return as 32-bit value. + * The 32-bit value is a "standard" C value (eg, of unknown endian). + */ +int floatnum_get_int(unsigned long *ret_val, const floatnum *flt); + +/* ptr will point to the Intel-format little-endian byte string after a + * successful call (eg, [0] should be the first byte output to the file). + */ +int floatnum_get_single(unsigned char *ptr, const floatnum *flt); +int floatnum_get_double(unsigned char *ptr, const floatnum *flt); +int floatnum_get_extended(unsigned char *ptr, const floatnum *flt); + +void floatnum_print(const floatnum *flt); #endif diff --git a/libyasm/symrec.c b/libyasm/symrec.c index 2c7947cd..5d545632 100644 --- a/libyasm/symrec.c +++ b/libyasm/symrec.c @@ -173,7 +173,9 @@ symrec_get_int_value(const symrec *sym, unsigned long *ret_val, *ret_val = sym->value.int_val; break; case SYM_CONSTANT_FLOAT: - *ret_val = floatnum_get_int(sym->value.flt); + /* FIXME: Line number on this error will be incorrect. */ + if (floatnum_get_int(ret_val, sym->value.flt)) + Error(_("Floating point value cannot fit in 32-bit single precision")); break; case SYM_LABEL: if (!bytecode_get_offset(sym->value.label.sect, diff --git a/libyasm/tests/Makefile.am b/libyasm/tests/Makefile.am index 9938959c..a5430acb 100644 --- a/libyasm/tests/Makefile.am +++ b/libyasm/tests/Makefile.am @@ -4,10 +4,12 @@ CFLAGS = @ANSI_CFLAGS@ if CHECK TESTS = \ - bytecode_test + bytecode_test \ + floatnum_test noinst_PROGRAMS = \ - bytecode_test + bytecode_test \ + floatnum_test else TESTS = noinst_PROGRAMS = @@ -16,6 +18,9 @@ endif bytecode_test_SOURCES = \ bytecode_test.c +floatnum_test_SOURCES = \ + floatnum_test.c + INCLUDES= -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/check LDADD = \ $(top_builddir)/check/libcheck.a \ diff --git a/libyasm/tests/floatnum_test.c b/libyasm/tests/floatnum_test.c new file mode 100644 index 00000000..cc093631 --- /dev/null +++ b/libyasm/tests/floatnum_test.c @@ -0,0 +1,122 @@ +/* $IdPath$ + * + */ +#include +#include "check.h" + +#include "bitvect.h" + +#include "floatnum.h" + +floatnum *flt; +void get_family_setup(void) +{ + + flt = malloc(sizeof(floatnum)); + flt->mantissa = BitVector_Create(64, TRUE); +} + +void get_family_teardown(void) +{ + BitVector_Destroy(flt->mantissa); + free(flt); +} + +void pi_setup(void) +{ + /* test value: 3.141592653589793 */ + /* 80-bit little endian hex: E9 BD 68 21 A2 DA 0F C9 00 40 */ + unsigned char val[] = {0xE9, 0xBD, 0x68, 0x21, 0xA2, 0xDA, 0x0F, 0xC9}; + unsigned char sign = 0; + unsigned short exp = 32767 + 1; + + BitVector_Block_Store(flt->mantissa, val, 8); + flt->sign = sign; + flt->exponent = exp; +} + +START_TEST(test_get_single_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0xDB, 0x0F, 0x49, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_single(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<4;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +START_TEST(test_get_double_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_double(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<8;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +START_TEST(test_get_extended_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0xE9, 0xBD, 0x68, 0x21, 0xA2, 0xDA, 0x0F, 0xC9, 0x00, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_extended(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<10;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +Suite *bytecode_suite(void) +{ + Suite *s = suite_create("floatnum"); + TCase *tc_get_single = tcase_create("get_single"); + TCase *tc_get_double = tcase_create("get_double"); + TCase *tc_get_extended = tcase_create("get_extended"); + + suite_add_tcase(s, tc_get_single); + tcase_add_test(tc_get_single, test_get_single_pi); + tcase_set_fixture(tc_get_single, get_family_setup, get_family_teardown); + + suite_add_tcase(s, tc_get_double); + tcase_add_test(tc_get_double, test_get_double_pi); + tcase_set_fixture(tc_get_double, get_family_setup, get_family_teardown); + + suite_add_tcase(s, tc_get_extended); + tcase_add_test(tc_get_extended, test_get_extended_pi); + tcase_set_fixture(tc_get_extended, get_family_setup, get_family_teardown); + + return s; +} + +int main(void) +{ + int nf; + Suite *s = bytecode_suite(); + SRunner *sr = srunner_create(s); + BitVector_Boot(); + srunner_run_all(sr, CRNORMAL); + nf = srunner_ntests_failed(sr); + srunner_free(sr); + suite_free(s); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/floatnum.c b/src/floatnum.c index bef9080e..50656aa6 100644 --- a/src/floatnum.c +++ b/src/floatnum.c @@ -3,6 +3,8 @@ * * Copyright (C) 2001 Peter Johnson * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * * This file is part of YASM. * * YASM is free software; you can redistribute it and/or modify @@ -29,8 +31,20 @@ #ifdef STDC_HEADERS # include +# include +#endif + +#include +#define _(String) gettext(String) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) #endif +#include "bitvect.h" +#include "file.h" + #include "errwarn.h" #include "floatnum.h" @@ -42,36 +56,224 @@ floatnum_new(char *str) floatnum *flt = malloc(sizeof(floatnum)); if (!flt) Fatal(FATAL_NOMEM); - /* TODO */ + + flt->mantissa = BitVector_Create(64, TRUE); + if (!flt->mantissa) + Fatal(FATAL_NOMEM); + + /* check for + or - character and skip */ + if (*str == '-') { + flt->sign = 1; + str++; + } else if (*str == '+') { + flt->sign = 0; + str++; + } else + flt->sign = 0; + return flt; } -unsigned long -floatnum_get_int(floatnum *flt) +int +floatnum_get_int(unsigned long *ret_val, const floatnum *flt) { - return 0; /* TODO */ + unsigned char t[4]; + + if (floatnum_get_single(t, flt)) + return 1; + + LOAD_LONG(*ret_val, &t[0]); + return 0; } -unsigned char * -floatnum_get_single(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "single precision" format: + * 32 bits: + * Bit 31 Bit 22 Bit 0 + * | | | + * seeeeeee emmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 127 exponent + * s = sign bit + * m = mantissa bits, bit 23 is an implied one bit. + */ +int +floatnum_get_single(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned long exponent = flt->exponent; + unsigned int *output; + unsigned char *buf; + unsigned int len; + + output = BitVector_Create(32, TRUE); + if (!output) + Fatal(FATAL_NOMEM); + + /* copy mantissa */ + BitVector_Interval_Copy(output, flt->mantissa, 0, 40, 23); + + /* round mantissa */ + BitVector_increment(output); + + if (BitVector_bit_test(output, 23)) { + /* overflowed, so divide mantissa by 2 (shift right) */ + BitVector_shift_right(output, 0); + /* and up the exponent (checking for overflow) */ + if (exponent+1 >= 0x10000) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + exponent++; + } + + /* adjust the exponent to bias 127 */ + exponent -= 32767-127; + if (exponent >= 256) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + + /* move exponent into place */ + BitVector_Chunk_Store(output, 8, 23, exponent); + + /* merge in sign bit */ + BitVector_Bit_Copy(output, 31, flt->sign); + + /* get little-endian bytes */ + buf = BitVector_Block_Read(output, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 4) + InternalError(__LINE__, __FILE__, + _("Length of 32-bit BitVector not 4")); + + /* copy to output */ + memcpy(ptr, buf, 4*sizeof(unsigned char)); + + /* free allocated resources */ + free(buf); + + BitVector_Destroy(output); + + return 0; } -unsigned char * -floatnum_get_double(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "double precision" format: + * 64 bits: + * bit 63 bit 51 bit 0 + * | | | + * seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 1023 exponent. + * s = sign bit. + * m = mantissa bits. Bit 52 is an implied one bit. + */ +int +floatnum_get_double(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned long exponent = flt->exponent; + unsigned int *output; + unsigned char *buf; + unsigned int len; + + output = BitVector_Create(64, TRUE); + if (!output) + Fatal(FATAL_NOMEM); + + /* copy mantissa */ + BitVector_Interval_Copy(output, flt->mantissa, 0, 11, 52); + + /* round mantissa */ + BitVector_increment(output); + + if (BitVector_bit_test(output, 52)) { + /* overflowed, so divide mantissa by 2 (shift right) */ + BitVector_shift_right(output, 0); + /* and up the exponent (checking for overflow) */ + if (exponent+1 >= 0x10000) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + exponent++; + } + + /* adjust the exponent to bias 1023 */ + exponent -= 32767-1023; + if (exponent >= 2048) { + BitVector_Destroy(output); + return 1; /* exponent too large */ + } + + /* move exponent into place */ + BitVector_Chunk_Store(output, 11, 52, exponent); + + /* merge in sign bit */ + BitVector_Bit_Copy(output, 63, flt->sign); + + /* get little-endian bytes */ + buf = BitVector_Block_Read(output, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 8) + InternalError(__LINE__, __FILE__, + _("Length of 64-bit BitVector not 8")); + + /* copy to output */ + memcpy(ptr, buf, 8*sizeof(unsigned char)); + + /* free allocated resources */ + free(buf); + + BitVector_Destroy(output); + + return 0; } -unsigned char * -floatnum_get_extended(unsigned char *ptr, floatnum *flt) +/* IEEE-754 (Intel) "extended precision" format: + * 80 bits: + * bit 79 bit 63 bit 0 + * | | | + * seeeeeee eeeeeeee mmmmmmmm m...m m...m m...m m...m m...m + * + * e = bias 16384 exponent + * m = 64 bit mantissa with NO implied bit! + * s = sign (for mantissa) + */ +int +floatnum_get_extended(unsigned char *ptr, const floatnum *flt) { - return ptr; /* TODO */ + unsigned short exponent = flt->exponent; + unsigned char *buf; + unsigned int len; + + /* Adjust the exponent to bias 16384 */ + exponent -= 0x4000; + if (exponent >= 0x8000) + return 1; /* exponent too large */ + + /* get little-endian bytes */ + buf = BitVector_Block_Read(flt->mantissa, &len); + if (!buf) + Fatal(FATAL_NOMEM); + if (len != 8) + InternalError(__LINE__, __FILE__, + _("Length of 64-bit BitVector not 8")); + + /* copy to output */ + memcpy(ptr, buf, 8*sizeof(unsigned char)); + + /* Save exponent and sign in proper location */ + SAVE_SHORT(&ptr[8], exponent); + if (flt->sign) + ptr[9] |= 0x80; + + /* free allocated resources */ + free(buf); + + return 0; } void -floatnum_print(floatnum *flt) +floatnum_print(const floatnum *flt) { /* TODO */ } diff --git a/src/floatnum.h b/src/floatnum.h index bbea9755..8ac8bf6f 100644 --- a/src/floatnum.h +++ b/src/floatnum.h @@ -3,6 +3,8 @@ * * Copyright (C) 2001 Peter Johnson * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * * This file is part of YASM. * * YASM is free software; you can redistribute it and/or modify @@ -22,16 +24,38 @@ #ifndef YASM_FLOATNUM_H #define YASM_FLOATNUM_H +/* 88-bit internal floating point format: + * xxxxxxxs eeeeeeee eeeeeeee m..m m..m m..m m..m m..m m..m m..m m..m + * Sign exponent mantissa (64 bits) + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0| + * 63 55 47 39 31 23 15 7 0 + * + * Only L.O. bit of Sign byte is significant. The rest is garbage. + * Exponent is bias 32767 exponent. + * Mantissa does NOT have an implied one bit (it's explicit). + */ typedef struct floatnum_s { - unsigned char extval[10]; /* float stored in extended precision */ + unsigned int *mantissa; /* Allocated to 64 bits */ + unsigned short exponent; + unsigned char sign; } floatnum; floatnum *floatnum_new(char *str); -unsigned long floatnum_get_int(floatnum *flt); -unsigned char *floatnum_get_single(unsigned char *ptr, floatnum *flt); -unsigned char *floatnum_get_double(unsigned char *ptr, floatnum *flt); -unsigned char *floatnum_get_extended(unsigned char *ptr, floatnum *flt); -void floatnum_print(floatnum *flt); +/* The get functions return nonzero if flt can't fit into that size format. */ + +/* Essentially a convert to single-precision and return as 32-bit value. + * The 32-bit value is a "standard" C value (eg, of unknown endian). + */ +int floatnum_get_int(unsigned long *ret_val, const floatnum *flt); + +/* ptr will point to the Intel-format little-endian byte string after a + * successful call (eg, [0] should be the first byte output to the file). + */ +int floatnum_get_single(unsigned char *ptr, const floatnum *flt); +int floatnum_get_double(unsigned char *ptr, const floatnum *flt); +int floatnum_get_extended(unsigned char *ptr, const floatnum *flt); + +void floatnum_print(const floatnum *flt); #endif diff --git a/src/symrec.c b/src/symrec.c index 2c7947cd..5d545632 100644 --- a/src/symrec.c +++ b/src/symrec.c @@ -173,7 +173,9 @@ symrec_get_int_value(const symrec *sym, unsigned long *ret_val, *ret_val = sym->value.int_val; break; case SYM_CONSTANT_FLOAT: - *ret_val = floatnum_get_int(sym->value.flt); + /* FIXME: Line number on this error will be incorrect. */ + if (floatnum_get_int(ret_val, sym->value.flt)) + Error(_("Floating point value cannot fit in 32-bit single precision")); break; case SYM_LABEL: if (!bytecode_get_offset(sym->value.label.sect, diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 9938959c..a5430acb 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -4,10 +4,12 @@ CFLAGS = @ANSI_CFLAGS@ if CHECK TESTS = \ - bytecode_test + bytecode_test \ + floatnum_test noinst_PROGRAMS = \ - bytecode_test + bytecode_test \ + floatnum_test else TESTS = noinst_PROGRAMS = @@ -16,6 +18,9 @@ endif bytecode_test_SOURCES = \ bytecode_test.c +floatnum_test_SOURCES = \ + floatnum_test.c + INCLUDES= -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/check LDADD = \ $(top_builddir)/check/libcheck.a \ diff --git a/src/tests/floatnum_test.c b/src/tests/floatnum_test.c new file mode 100644 index 00000000..cc093631 --- /dev/null +++ b/src/tests/floatnum_test.c @@ -0,0 +1,122 @@ +/* $IdPath$ + * + */ +#include +#include "check.h" + +#include "bitvect.h" + +#include "floatnum.h" + +floatnum *flt; +void get_family_setup(void) +{ + + flt = malloc(sizeof(floatnum)); + flt->mantissa = BitVector_Create(64, TRUE); +} + +void get_family_teardown(void) +{ + BitVector_Destroy(flt->mantissa); + free(flt); +} + +void pi_setup(void) +{ + /* test value: 3.141592653589793 */ + /* 80-bit little endian hex: E9 BD 68 21 A2 DA 0F C9 00 40 */ + unsigned char val[] = {0xE9, 0xBD, 0x68, 0x21, 0xA2, 0xDA, 0x0F, 0xC9}; + unsigned char sign = 0; + unsigned short exp = 32767 + 1; + + BitVector_Block_Store(flt->mantissa, val, 8); + flt->sign = sign; + flt->exponent = exp; +} + +START_TEST(test_get_single_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0xDB, 0x0F, 0x49, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_single(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<4;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +START_TEST(test_get_double_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_double(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<8;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +START_TEST(test_get_extended_pi) +{ + unsigned char outval[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char correct[] = {0xE9, 0xBD, 0x68, 0x21, 0xA2, 0xDA, 0x0F, 0xC9, 0x00, 0x40}; + int i; + + pi_setup(); + + fail_unless(floatnum_get_extended(outval, flt) == 0, + "Should not fail on this value"); + + /* Compare result with what we should get. */ + for (i=0;i<10;i++) + fail_unless(outval[i] == correct[i], "Invalid result generated"); +} +END_TEST + +Suite *bytecode_suite(void) +{ + Suite *s = suite_create("floatnum"); + TCase *tc_get_single = tcase_create("get_single"); + TCase *tc_get_double = tcase_create("get_double"); + TCase *tc_get_extended = tcase_create("get_extended"); + + suite_add_tcase(s, tc_get_single); + tcase_add_test(tc_get_single, test_get_single_pi); + tcase_set_fixture(tc_get_single, get_family_setup, get_family_teardown); + + suite_add_tcase(s, tc_get_double); + tcase_add_test(tc_get_double, test_get_double_pi); + tcase_set_fixture(tc_get_double, get_family_setup, get_family_teardown); + + suite_add_tcase(s, tc_get_extended); + tcase_add_test(tc_get_extended, test_get_extended_pi); + tcase_set_fixture(tc_get_extended, get_family_setup, get_family_teardown); + + return s; +} + +int main(void) +{ + int nf; + Suite *s = bytecode_suite(); + SRunner *sr = srunner_create(s); + BitVector_Boot(); + srunner_run_all(sr, CRNORMAL); + nf = srunner_ntests_failed(sr); + srunner_free(sr); + suite_free(s); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}