mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
1758 lines
53 KiB
C
1758 lines
53 KiB
C
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
#include <math.h>
|
||
#include <float.h>
|
||
#include <stdint.h>
|
||
#if defined(TEST_TARGET_floatl)
|
||
#include <varch/command.h>
|
||
#include <varch/unitt.h>
|
||
#include <varch/floatl.h>
|
||
#else
|
||
#include "init.h"
|
||
#include "command.h"
|
||
#include "unitt.h"
|
||
#include "kern.h"
|
||
#include "floatl.h"
|
||
#endif
|
||
|
||
typedef union {
|
||
float float_;
|
||
uint32_t int_;
|
||
struct
|
||
{
|
||
uint32_t mantissa : 23;
|
||
uint32_t exponent : 8;
|
||
uint32_t sign : 1;
|
||
};
|
||
} float_u;
|
||
typedef union
|
||
{
|
||
double double_;
|
||
uint64_t int_;
|
||
struct
|
||
{
|
||
uint64_t mantissa : 52;
|
||
uint64_t exponent : 11;
|
||
uint64_t sign : 1;
|
||
} ;
|
||
#if defined(FLOATL_USE_64BITS)
|
||
floatl floatl_;
|
||
#endif
|
||
} double_u;
|
||
|
||
static char buffer[1024] = {0};
|
||
|
||
static unsigned int b2o(unsigned int bits)
|
||
{
|
||
unsigned int group = bits / 10;
|
||
unsigned int index = bits % 10;
|
||
unsigned int offset = 0;
|
||
unsigned int o = 0;
|
||
|
||
if (index < 4) offset = 1;
|
||
else if (index < 7) offset = 2;
|
||
else offset = 3;
|
||
|
||
return group * 3 + offset;
|
||
}
|
||
|
||
// 去除字符串首尾的空白字符
|
||
static void trim(char *str)
|
||
{
|
||
int start = 0;
|
||
int end = strlen(str) - 1;
|
||
|
||
// 去除字符串开头的空白字符
|
||
while (isspace(str[start]))
|
||
{
|
||
start++;
|
||
}
|
||
|
||
// 去除字符串末尾的空白字符
|
||
while (end >= start && isspace(str[end]))
|
||
{
|
||
end--;
|
||
}
|
||
|
||
// 移动字符到字符串开头
|
||
int i;
|
||
for (i = 0; start <= end; i++, start++)
|
||
{
|
||
str[i] = str[start];
|
||
}
|
||
str[i] = '\0';
|
||
}
|
||
|
||
// 字符串转换为 double 类型
|
||
double double_from_string(const char *str)
|
||
{
|
||
char temp[100];
|
||
strcpy(temp, str);
|
||
trim(temp);
|
||
|
||
int len = strlen(temp);
|
||
if (len == 0)
|
||
{
|
||
return 0.0;
|
||
}
|
||
|
||
int sign = 1;
|
||
int i = 0;
|
||
|
||
// 处理符号
|
||
if (temp[0] == '-')
|
||
{
|
||
sign = -1;
|
||
i++;
|
||
}
|
||
else if (temp[0] == '+')
|
||
{
|
||
i++;
|
||
}
|
||
|
||
// 检查是否为十六进制
|
||
if (len >= i + 2 && temp[i] == '0' && (temp[i + 1] == 'x' || temp[i + 1] == 'X'))
|
||
{
|
||
i += 2;
|
||
double value = 0.0;
|
||
int point_found = 0;
|
||
double fraction = 1.0;
|
||
int exp_sign = 1;
|
||
int exp_value = 0;
|
||
|
||
// 解析十六进制数字部分
|
||
for (; i < len; i++)
|
||
{
|
||
if (temp[i] == '.')
|
||
{
|
||
point_found = 1;
|
||
continue;
|
||
}
|
||
else if (temp[i] == 'p' || temp[i] == 'P')
|
||
{
|
||
i++;
|
||
if (i < len && temp[i] == '-')
|
||
{
|
||
exp_sign = -1;
|
||
i++;
|
||
}
|
||
else if (i < len && temp[i] == '+')
|
||
{
|
||
i++;
|
||
}
|
||
// 解析十六进制指数部分
|
||
for (; i < len; i++)
|
||
{
|
||
if (isdigit(temp[i]))
|
||
{
|
||
exp_value = exp_value * 10 + (temp[i] - '0');
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (isdigit(temp[i]))
|
||
{
|
||
value = value * 16 + (temp[i] - '0');
|
||
}
|
||
else if (temp[i] >= 'a' && temp[i] <= 'f')
|
||
{
|
||
value = value * 16 + (temp[i] - 'a' + 10);
|
||
}
|
||
else if (temp[i] >= 'A' && temp[i] <= 'F')
|
||
{
|
||
value = value * 16 + (temp[i] - 'A' + 10);
|
||
}
|
||
|
||
if (point_found)
|
||
{
|
||
fraction *= 16;
|
||
}
|
||
}
|
||
|
||
value /= fraction;
|
||
value *= pow(2, exp_sign * exp_value);
|
||
return sign * value;
|
||
}
|
||
|
||
// 处理十进制和指数计数法
|
||
double value = 0.0;
|
||
int point_found = 0;
|
||
double fraction = 1.0;
|
||
int exp_sign = 1;
|
||
int exp_value = 0;
|
||
|
||
for (; i < len; i++)
|
||
{
|
||
if (temp[i] == '.')
|
||
{
|
||
point_found = 1;
|
||
continue;
|
||
}
|
||
else if (temp[i] == 'e' || temp[i] == 'E')
|
||
{
|
||
i++;
|
||
if (i < len && temp[i] == '-')
|
||
{
|
||
exp_sign = -1;
|
||
i++;
|
||
}
|
||
else if (i < len && temp[i] == '+')
|
||
{
|
||
i++;
|
||
}
|
||
// 解析十进制指数部分
|
||
for (; i < len; i++)
|
||
{
|
||
if (isdigit(temp[i]))
|
||
{
|
||
exp_value = exp_value * 10 + (temp[i] - '0');
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (isdigit(temp[i]))
|
||
{
|
||
value = value * 10 + (temp[i] - '0');
|
||
}
|
||
|
||
if (point_found)
|
||
{
|
||
fraction *= 10;
|
||
}
|
||
}
|
||
|
||
value /= fraction;
|
||
value *= pow(10, exp_sign * exp_value);
|
||
return sign * value;
|
||
}
|
||
|
||
static double double_from_uint64(uint64_t value)
|
||
{
|
||
double_u v = {.int_ = value};
|
||
return v.double_;
|
||
}
|
||
|
||
float double_to_float(double d)
|
||
{
|
||
// 联合体用于直接操作二进制位
|
||
union { double d; uint64_t u; } du = { .d = d };
|
||
union { float f; uint32_t u; } fu;
|
||
|
||
// 提取double的符号、指数、尾数
|
||
uint64_t sign = (du.u >> 63) & 0x1;
|
||
int64_t exp = ((du.u >> 52) & 0x7FF) - 1023; // 原始指数
|
||
uint64_t mant = du.u & 0x000FFFFFFFFFFFFF; // 52位尾数
|
||
|
||
// 特殊值处理(NaN/Inf)
|
||
if (exp == 1024) {
|
||
fu.u = (sign << 31) | 0x7F800000 | (mant ? 0x7FFFFF : 0);
|
||
return fu.f;
|
||
}
|
||
|
||
// 调整指数到float范围(-126~127)
|
||
exp += 127; // 转换为float偏置指数
|
||
if (exp > 255) { // 上溢返回无穷大
|
||
fu.u = (sign << 31) | 0x7F800000;
|
||
return fu.f;
|
||
} else if (exp < 0) { // 下溢返回0
|
||
fu.u = sign << 31;
|
||
return fu.f;
|
||
}
|
||
|
||
// 尾数处理(隐含的1 + 52位尾数)
|
||
uint64_t extended_mant = mant | 0x0010000000000000; // 恢复隐含的1
|
||
uint32_t float_mant = (extended_mant >> 29); // 保留高23位
|
||
|
||
// 舍入处理(检查第29位)
|
||
uint32_t round_bits = extended_mant & 0x1FFFFFFF;
|
||
if (round_bits > 0x10000000 ||
|
||
(round_bits == 0x10000000 && (float_mant & 0x1))) {
|
||
float_mant += 1;
|
||
if (float_mant & 0x00800000) { // 尾数进位导致指数进位
|
||
float_mant >>= 1;
|
||
exp += 1;
|
||
if (exp > 255) { // 指数二次上溢
|
||
fu.u = (sign << 31) | 0x7F800000;
|
||
return fu.f;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 组合结果
|
||
fu.u = (sign << 31) | (exp << 23) | (float_mant & 0x007FFFFF);
|
||
return fu.f;
|
||
}
|
||
|
||
// 将 double 的二进制表示转换为 uint64_t
|
||
// typedef unsigned long long uint64_t;
|
||
// typedef long long int64_t;
|
||
|
||
uint64_t double_to_bits(double x) {
|
||
return *((uint64_t*)&x);
|
||
}
|
||
|
||
double bits_to_double(uint64_t bits) {
|
||
return *((double*)&bits);
|
||
}
|
||
|
||
// double_ceil 实现
|
||
double double_ceil(double x) {
|
||
uint64_t bits = double_to_bits(x);
|
||
int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;
|
||
|
||
if (exponent < 0) {
|
||
// 如果指数为负,说明 x 在 (-1, 1) 之间
|
||
if (x > 0) {
|
||
return 1.0;
|
||
} else if (x == 0.0) {
|
||
return 0.0;
|
||
} else {
|
||
return -0.0;
|
||
}
|
||
}
|
||
|
||
int shift = 52 - exponent;
|
||
if (shift <= 0) {
|
||
// 如果 x 已经是整数,直接返回 x
|
||
return x;
|
||
}
|
||
|
||
uint64_t mask = (1ULL << shift) - 1;
|
||
uint64_t truncated = bits & ~mask;
|
||
|
||
double result = bits_to_double(truncated);
|
||
if (result < x) {
|
||
result += 1.0;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// double_floor 实现
|
||
double double_floor(double x) {
|
||
uint64_t bits = double_to_bits(x);
|
||
int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;
|
||
|
||
if (exponent < 0) {
|
||
// 如果指数为负,说明 x 在 (-1, 1) 之间
|
||
if (x >= 0) {
|
||
return 0.0;
|
||
} else {
|
||
return -1.0;
|
||
}
|
||
}
|
||
|
||
int shift = 52 - exponent;
|
||
if (shift <= 0) {
|
||
// 如果 x 已经是整数,直接返回 x
|
||
return x;
|
||
}
|
||
|
||
uint64_t mask = (1ULL << shift) - 1;
|
||
uint64_t truncated = bits & ~mask;
|
||
|
||
double result = bits_to_double(truncated);
|
||
if (result > x) {
|
||
result -= 1.0;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// double_round 实现
|
||
double double_round(double x) {
|
||
uint64_t bits = double_to_bits(x);
|
||
int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;
|
||
|
||
if (exponent < 0) {
|
||
// 如果指数为负,说明 x 在 (-1, 1) 之间
|
||
if (x > 0) {
|
||
return (x >= 0.5) ? 1.0 : 0.0;
|
||
} else {
|
||
return (x <= -0.5) ? -1.0 : -0.0;
|
||
}
|
||
}
|
||
|
||
int shift = 52 - exponent;
|
||
if (shift <= 0) {
|
||
// 如果 x 已经是整数,直接返回 x
|
||
return x;
|
||
}
|
||
|
||
uint64_t mask = (1ULL << shift) - 1;
|
||
uint64_t truncated = bits & ~mask;
|
||
|
||
double result = bits_to_double(truncated);
|
||
uint64_t fractional = bits & mask;
|
||
|
||
if (fractional >= (1ULL << (shift - 1))) {
|
||
result += 1.0;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static const char* float_show_raw(float x)
|
||
{
|
||
float_u a = {.float_ = x};
|
||
|
||
printf("float --------------\r\n");
|
||
printf("a.sign %u\r\n", a.sign);
|
||
printf("a.exponent %d\r\n", (int32_t)((int32_t)a.exponent - 127));
|
||
|
||
uint32_t mvalue = a.mantissa;
|
||
for (int i = 23 - 1; i >= 0; i--)
|
||
{
|
||
((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
|
||
}
|
||
printf("\r\n");
|
||
|
||
return 0;
|
||
}
|
||
|
||
static const char* double_show_raw(double x)
|
||
{
|
||
double_u a = {.double_ = x};
|
||
|
||
printf("[double]: sign(%u) exponent=%d\r\n", a.sign, (int32_t)((int32_t)a.exponent - 1023));
|
||
|
||
uint64_t mvalue = a.mantissa;
|
||
for (int i = 52 - 1; i >= 0; i--)
|
||
{
|
||
((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
|
||
}
|
||
printf("\r\n");
|
||
|
||
return 0;
|
||
}
|
||
|
||
static const char* floatl_show_raw(floatl a, char *buffer, int size, const char *format)
|
||
{
|
||
static char sbuffer[100] = {0};
|
||
|
||
char *base = sbuffer;
|
||
int index = 0;
|
||
int length = 100;
|
||
|
||
printf("[floatl]: sign(%u) exponent=%d\r\n", a.sign, (int32_t)((int32_t)a.exponent - __FLOATL_EXP_MID_VALUE__));
|
||
|
||
uint32_t mvalue = a.mantissa;
|
||
for (int i = __FLOATL_MANT_HIGH_BITS__ - 1; i >= 0; i--)
|
||
{
|
||
((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
|
||
}
|
||
for (int m = __FLOATL_MANT_PARTS__ - 1; m >= 0; m--)
|
||
{
|
||
mvalue = a.mantissas[m];
|
||
for (int i = 31; i >= 0; i--)
|
||
{
|
||
((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
|
||
}
|
||
}
|
||
printf("\r\n");
|
||
|
||
return base;
|
||
}
|
||
|
||
static double double_random(void)
|
||
{
|
||
uint64_t raw_bits = 0;
|
||
uint32_t sign = 0;
|
||
uint32_t exponent = 0;
|
||
uint64_t mantissa = 0;
|
||
double result;
|
||
|
||
do {
|
||
// 定义一个 64 位无符号整数来模拟 double 的存储
|
||
raw_bits = 0;
|
||
|
||
// 随机生成符号位(0 或 1)
|
||
sign = rand() % 2;
|
||
raw_bits |= (uint64_t)sign << 63;
|
||
|
||
// 随机生成指数位(范围 0 - 2047)
|
||
exponent = rand() % 2048;
|
||
raw_bits |= (uint64_t)exponent << 52;
|
||
|
||
// 随机生成尾数位(范围 0 - 4503599627370495)
|
||
mantissa = (uint64_t)rand() << 32 | rand();
|
||
raw_bits |= mantissa & 0xFFFFFFFFFFFFF;
|
||
|
||
// 将 64 位无符号整数转换为 double 类型
|
||
memcpy(&result, &raw_bits, sizeof(double));
|
||
|
||
} while (isnan(result) || isinf(result) || (exponent == 0 && mantissa != 0));
|
||
|
||
return result;
|
||
}
|
||
|
||
static floatl floatl_random(void)
|
||
{
|
||
floatl random = FLOATL_CONST_0;
|
||
|
||
#if 0
|
||
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
||
{
|
||
random.u32[i] = rand();
|
||
}
|
||
#else
|
||
double r = double_random();
|
||
memcpy(&random, &r, sizeof(double));
|
||
#endif
|
||
return random;
|
||
}
|
||
|
||
#define DOUBLE_RFLAG_NO_PLUS 0x01
|
||
#define DOUBLE_RFLAG_NO_SUB 0x02
|
||
#define DOUBLE_RFLAG_NO_ZERO 0x04
|
||
#define DOUBLE_RFLAG_NO_WIDTH 0x08
|
||
#define DOUBLE_RFLAG_NO_PRECISION 0x10
|
||
#define DOUBLE_RFLAG_EXCLUDE_F 0x10
|
||
#define DOUBLE_RFLAG_EXCLUDE_A 0x20
|
||
#define DOUBLE_RFLAG_EXCLUDE_E 0x40
|
||
|
||
static void double_random_format(char *format, int size, int flags)
|
||
{
|
||
const char baseArray[6] = {'F', 'f', 'A', 'a', 'E', 'e'};
|
||
int flag_plus = rand() % 2;
|
||
int flag_sub = rand() % 2;
|
||
int flag_zero = rand() % 2;
|
||
int width = rand() % 50;
|
||
int precision = rand() % 10;
|
||
int base = 0;
|
||
|
||
int len = 0;
|
||
char *f = format;
|
||
|
||
*f++ = '%';
|
||
if (flag_plus && !(flags & DOUBLE_RFLAG_NO_PLUS)) *f++ = '+';
|
||
if (flag_sub && !(flags & DOUBLE_RFLAG_NO_SUB)) *f++ = '-';
|
||
if (flag_zero && !(flags & DOUBLE_RFLAG_NO_ZERO)) *f++ = '0';
|
||
if (!(flags & DOUBLE_RFLAG_NO_WIDTH))
|
||
{
|
||
len = sprintf(f, "%d", width);
|
||
f += len;
|
||
}
|
||
if (!(flags & DOUBLE_RFLAG_NO_PRECISION))
|
||
{
|
||
*f++ = '.';
|
||
len = sprintf(f, "%d", precision);
|
||
f += len;
|
||
}
|
||
if ((flags & DOUBLE_RFLAG_EXCLUDE_F) && (flags & DOUBLE_RFLAG_EXCLUDE_A) && (flags & DOUBLE_RFLAG_EXCLUDE_E))
|
||
{
|
||
flags |= DOUBLE_RFLAG_EXCLUDE_F;
|
||
}
|
||
while (1)
|
||
{
|
||
base = rand() % sizeof(baseArray);
|
||
if (((flags & DOUBLE_RFLAG_EXCLUDE_F) && (baseArray[base] == 'F' || baseArray[base] == 'f')) ||
|
||
((flags & DOUBLE_RFLAG_EXCLUDE_A) && (baseArray[base] == 'A' || baseArray[base] == 'a')) ||
|
||
((flags & DOUBLE_RFLAG_EXCLUDE_E) && (baseArray[base] == 'E' || baseArray[base] == 'e'))
|
||
) continue;
|
||
break;
|
||
}
|
||
*f++ = baseArray[base];
|
||
*f = '\0';
|
||
}
|
||
|
||
static int64_t uint64_cmp(uint64_t *v1, uint64_t *v2)
|
||
{
|
||
int64_t error = *v1 - *v2;
|
||
if (error < 0) error = -error;
|
||
return error;
|
||
}
|
||
|
||
static int double_equal(double *v1, double *v2)
|
||
{
|
||
double_u *u1 = (double_u *)v1;
|
||
double_u *u2 = (double_u *)v2;
|
||
|
||
// 都认作0,即为相等
|
||
if (u1->exponent == 0 && u2->exponent == 0) return 1;
|
||
|
||
// 允许一位舍入误差
|
||
if (uint64_cmp(&u1->int_, &u2->int_) > 1) return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static void double_print(FILE *file, double value)
|
||
{
|
||
double_u v = {.double_ = value};
|
||
uint64_t mvalue = v.mantissa;
|
||
fprintf(file, "// [double]<%llu>(%u,%d,%llu){", (uint64_t)v.int_, (uint32_t)v.sign, (int32_t)((int32_t)v.exponent - 1023), (uint64_t)v.mantissa);
|
||
for (int i = 51; i >= 0; i--)
|
||
{
|
||
((mvalue >> (i)) & 1) ? fputc('1', file) : fputc('0', file);
|
||
}
|
||
fprintf(file, "}\n");
|
||
}
|
||
|
||
/************************************************************************************/
|
||
/************************************* Unit Test ************************************/
|
||
/************************************************************************************/
|
||
|
||
// #define EXIT_TEST
|
||
extern uint64_t unitt_clock(void);
|
||
|
||
static void generate_filename(char *filename, uint32_t size)
|
||
{
|
||
time_t current_time = time(NULL);
|
||
if (current_time == ((time_t)-1))
|
||
{
|
||
fprintf(stderr, "Failed to get current time\n");
|
||
return;
|
||
}
|
||
|
||
struct tm *local_time = localtime(¤t_time);
|
||
if (local_time == NULL) {
|
||
fprintf(stderr, "Failed to convert to local time\n");
|
||
return;
|
||
}
|
||
|
||
strftime(filename, size, "%Y%m%d_%H%M%S.txt", local_time);
|
||
}
|
||
|
||
static FILE** error_file(int index)
|
||
{
|
||
static FILE *table[6] = {NULL, NULL, NULL, NULL}; // stdout
|
||
FILE **file = NULL;
|
||
|
||
index = index % 6;
|
||
file = &table[index];
|
||
if (!*file)
|
||
{
|
||
char filename[64] = "built/";
|
||
filename[6] = '0' + index;
|
||
filename[7] = '_';
|
||
generate_filename(filename + 8, sizeof(filename) - 8);
|
||
*file = fopen(filename, "a");
|
||
if (*file == NULL)
|
||
{
|
||
perror("Failed to open output file");
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
return file;
|
||
}
|
||
|
||
static void error_record_cal(char op, floatl a, floatl b, floatl c, double x, double y, double z)
|
||
{
|
||
FILE **file = NULL;
|
||
int index = 0;
|
||
|
||
if (op == '+') index = 0;
|
||
else if (op == '-') index = 1;
|
||
else if (op == '*') index = 2;
|
||
else if (op == '/') index = 3;
|
||
|
||
file = error_file(index);
|
||
|
||
uint64_t m, n;
|
||
memcpy(&m, &a, sizeof(m));
|
||
memcpy(&n, &b, sizeof(n));
|
||
double_print(*file, x);
|
||
double_print(*file, y);
|
||
double_print(*file, z);
|
||
double_print(*file, *(double *)(&c));
|
||
fprintf(*file, "{.v1.int_ = 0x%016llX, .v2.int_ = 0x%016llX}, // %lf %c %lf = %lf\n\n", m, n, x, op, y, z);
|
||
}
|
||
|
||
static void error_record_print(floatl a, char *format, char *s1, char *s2)
|
||
{
|
||
FILE **file = error_file(4);
|
||
|
||
double_print(*file, *(double *)(&a));
|
||
fprintf(*file, "// %s\n", format);
|
||
fprintf(*file, "// %s\n", s1);
|
||
fprintf(*file, "// %s\n", s2);
|
||
fprintf(*file, "\n\n");
|
||
}
|
||
|
||
static void error_record_from(floatl a, floatl b, char *format, char *s1, char *s2)
|
||
{
|
||
FILE **file = error_file(5);
|
||
|
||
double_print(*file, *(double *)(&a));
|
||
double_print(*file, *(double *)(&b));
|
||
fprintf(*file, "// %s\n", format);
|
||
fprintf(*file, "// %s\n", s1);
|
||
fprintf(*file, "// %s\n", s2);
|
||
fprintf(*file, "\n\n");
|
||
}
|
||
|
||
static int u_test_add(void)
|
||
{
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
floatl b = floatl_random();
|
||
floatl c = FLOATL_CONST_0;
|
||
|
||
double x = 0;
|
||
double y = 0;
|
||
double z = 0;
|
||
|
||
memcpy(&x, &a, sizeof(x));
|
||
memcpy(&y, &b, sizeof(y));
|
||
|
||
{ c = floatl_add(a, b); z = x + y; }
|
||
|
||
if (!double_equal(&c, &z))
|
||
{
|
||
error_record_cal('+', a, b, c, x, y, z);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static int u_test_sub(void)
|
||
{
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
floatl b = floatl_random();
|
||
floatl c = FLOATL_CONST_0;
|
||
|
||
double x = 0;
|
||
double y = 0;
|
||
double z = 0;
|
||
|
||
memcpy(&x, &a, sizeof(x));
|
||
memcpy(&y, &b, sizeof(y));
|
||
|
||
{ c = floatl_sub(a, b); z = x - y; }
|
||
|
||
if (!double_equal(&c, &z))
|
||
{
|
||
error_record_cal('-', a, b, c, x, y, z);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static int u_test_mul(void)
|
||
{
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
floatl b = floatl_random();
|
||
floatl c = FLOATL_CONST_0;
|
||
|
||
double x = 0;
|
||
double y = 0;
|
||
double z = 0;
|
||
|
||
memcpy(&x, &a, sizeof(x));
|
||
memcpy(&y, &b, sizeof(y));
|
||
|
||
{ c = floatl_mul(a, b); z = x * y; }
|
||
|
||
if (!double_equal(&c, &z))
|
||
{
|
||
error_record_cal('*', a, b, c, x, y, z);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static int u_test_div(void)
|
||
{
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
floatl b = floatl_random();
|
||
floatl c = FLOATL_CONST_0;
|
||
|
||
double x = 0;
|
||
double y = 0;
|
||
double z = 0;
|
||
|
||
memcpy(&x, &a, sizeof(x));
|
||
memcpy(&y, &b, sizeof(y));
|
||
|
||
if (fabs(y) < 1e-9) continue;
|
||
|
||
{ c = floatl_div(a, b); z = x / y; }
|
||
|
||
if (!double_equal(&c, &z))
|
||
{
|
||
error_record_cal('/', a, b, c, x, y, z);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static int u_test_print(void)
|
||
{
|
||
static char format[100] = {0};
|
||
static char buffer_f[100] = {0};
|
||
static char buffer_d[100] = {0};
|
||
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
double x = 0;
|
||
int len_f = 0;
|
||
int len_d = 0;
|
||
|
||
memcpy(&x, &a, sizeof(x));
|
||
double_random_format(format, sizeof(format), DOUBLE_RFLAG_EXCLUDE_F | DOUBLE_RFLAG_EXCLUDE_A);
|
||
|
||
len_f = floatl_print(a, buffer_f, sizeof(buffer_f), format);
|
||
len_d = snprintf(buffer_d, sizeof(buffer_d), format, x);
|
||
|
||
if (len_f <= 0 || len_d <= 0) continue;
|
||
|
||
if (len_f != len_d || strcmp(buffer_f, buffer_d) != 0)
|
||
{
|
||
error_record_print(a, format, buffer_f, buffer_d);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static int u_test_from(void)
|
||
{
|
||
static char format[100] = {0};
|
||
static char buffer_0[100] = {0};
|
||
static char buffer_1[100] = {0};
|
||
|
||
for (int i = 0; i < 1000; i++)
|
||
{
|
||
floatl a = floatl_random();
|
||
floatl b = FLOATL_CONST_0;
|
||
|
||
double_random_format(format, sizeof(format), DOUBLE_RFLAG_NO_PLUS | DOUBLE_RFLAG_NO_SUB | DOUBLE_RFLAG_NO_ZERO | DOUBLE_RFLAG_NO_WIDTH);
|
||
|
||
if (floatl_print(a, buffer_0, sizeof(buffer_0), format) <= 0) continue;
|
||
|
||
b = floatl_from(buffer_0);
|
||
|
||
floatl_print(b, buffer_1, sizeof(buffer_1), format);
|
||
|
||
if (floatl_isnan(b) || strncmp(buffer_0, buffer_1, sizeof(buffer_0)))
|
||
{
|
||
error_record_from(a, b, format, buffer_0, buffer_1);
|
||
#if defined (EXIT_TEST)
|
||
exit(0);
|
||
#endif
|
||
return UNITT_E_FAIL;
|
||
}
|
||
}
|
||
|
||
return UNITT_E_OK;
|
||
}
|
||
|
||
static void unitt_task(void)
|
||
{
|
||
static UNITT_TCASE rand_tests[] = {
|
||
#if defined(FLOATL_USE_64BITS)
|
||
UNITT_TCASE(u_test_add),
|
||
UNITT_TCASE(u_test_sub),
|
||
UNITT_TCASE(u_test_mul),
|
||
UNITT_TCASE(u_test_div),
|
||
UNITT_TCASE(u_test_print),
|
||
#endif
|
||
UNITT_TCASE(u_test_from),
|
||
};
|
||
|
||
static UNITT suites[] = {
|
||
{ "floatl suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
|
||
};
|
||
|
||
UNITT_EXE(suites);
|
||
}
|
||
|
||
/************************************************************************************/
|
||
/************************************* Base Test ************************************/
|
||
/************************************************************************************/
|
||
|
||
/*----------------------------------------------------------------------------------*/
|
||
/* Generate configuration [START] */
|
||
/*----------------------------------------------------------------------------------*/
|
||
|
||
typedef struct
|
||
{
|
||
uint32_t buffer[256];
|
||
|
||
uint32_t FLOATL_BIT_PARTS__; // bits
|
||
uint32_t FLOATL_U32_PARTS__; // FLOATL_BIT_PARTS__ / 32 ---- FLOATL_BIT_PARTS__ >> 5
|
||
uint32_t FLOATL_U16_PARTS__; // FLOATL_BIT_PARTS__ / 16 ---- FLOATL_BIT_PARTS__ >> 4
|
||
uint32_t FLOATL_EXP_BITS__; // floatl_cfg_gen_exp(FLOATL_BIT_PARTS__)
|
||
uint32_t FLOATL_MANT_BITS__; // FLOATL_BIT_PARTS__ - FLOATL_EXP_BITS__ - 1
|
||
uint32_t FLOATL_MANT_PARTS__; // FLOATL_MANT_BITS__ / 32 ---- FLOATL_MANT_BITS__ >> 5
|
||
uint32_t FLOATL_MANT_HIGH_BITS__; // FLOATL_MANT_BITS__ % 32 ---- FLOATL_MANT_BITS__ & 0x1F
|
||
uint32_t FLOATL_EXP_MID_VALUE__; // 2^(FLOATL_EXP_BITS__-1) - 1
|
||
uint32_t FLOATL_EXP_WHL_VALUE__; // 2^(FLOATL_EXP_BITS__) - 1
|
||
|
||
uint32_t FLOATL2_BIT_PARTS__; // FLOATL_BIT_PARTS__ * 2
|
||
uint32_t FLOATL2_U32_PARTS__; // FLOATL2_BIT_PARTS__ / 32 ---- FLOATL2_BIT_PARTS__ >> 5
|
||
uint32_t FLOATL2_U16_PARTS__; // FLOATL2_BIT_PARTS__ / 16 ---- FLOATL2_BIT_PARTS__ >> 4
|
||
|
||
uint32_t FLOATL_MANT_DIG__; // FLOATL_MANT_BITS__ + 1
|
||
int32_t FLOATL_MIN_EXP__; // - FLOATL_EXP_MID_VALUE__ + 2
|
||
uint32_t FLOATL_MAX_EXP__; // FLOATL_EXP_MID_VALUE__ + 1
|
||
uint32_t FLOATL_DIG__; // floor(log10(2) * FLOATL_MANT_DIG__)
|
||
uint32_t FLOATL_DECIMAL_DIG__; // ceil(log10(2) * FLOATL_MANT_DIG__) + 1
|
||
int32_t FLOATL_MIN_10_EXP__; // ceil(log10(2) * (1 - FLOATL_EXP_MID_VALUE__))
|
||
uint32_t FLOATL_MAX_10_EXP__; // - FLOATL_MIN_10_EXP__ + 1
|
||
} FLINFO;
|
||
|
||
typedef struct
|
||
{
|
||
uint32_t exp;
|
||
char *mantBin;
|
||
} FLCSTE; // const pow10(n)
|
||
|
||
static FLCSTE fle_list[] = {
|
||
{.exp = 0, .mantBin = "0000000000000000000000000000000000000000000000000000"},
|
||
{.exp = 3, .mantBin = "0100000000000000000000000000000000000000000000000000"},
|
||
{.exp = 6, .mantBin = "1001000000000000000000000000000000000000000000000000"},
|
||
{.exp = 9, .mantBin = "1111010000000000000000000000000000000000000000000000"},
|
||
{.exp = 13, .mantBin = "0011100010000000000000000000000000000000000000000000"},
|
||
{.exp = 16, .mantBin = "1000011010100000000000000000000000000000000000000000"},
|
||
{.exp = 19, .mantBin = "1110100001001000000000000000000000000000000000000000"},
|
||
{.exp = 23, .mantBin = "0011000100101101000000000000000000000000000000000000"},
|
||
{.exp = 26, .mantBin = "0111110101111000010000000000000000000000000000000000"},
|
||
{.exp = 29, .mantBin = "1101110011010110010100000000000000000000000000000000"},
|
||
{.exp = 33, .mantBin = "0010101000000101111100100000000000000000000000000000"},
|
||
{.exp = 36, .mantBin = "0111010010000111011011101000000000000000000000000000"},
|
||
{.exp = 39, .mantBin = "1101000110101001010010100010000000000000000000000000"},
|
||
{.exp = 43, .mantBin = "0010001100001001110011100101010000000000000000000000"},
|
||
{.exp = 46, .mantBin = "0110101111001100010000011110100100000000000000000000"},
|
||
{.exp = 49, .mantBin = "1100011010111111010100100110001101000000000000000000"},
|
||
};
|
||
|
||
// e = 2.8625 * log2(bits) + 0.0111 * bits - 6.6730
|
||
uint32_t floatl_cfg_gen_exp(uint32_t bits)
|
||
{
|
||
const int y = bits;
|
||
int mantissa = 0;
|
||
int exponent = 0;
|
||
int sign = 1;
|
||
|
||
for (int i = y; i > 0; i--)
|
||
{
|
||
if (log2(i) * 2 < (y - i - 1))
|
||
{
|
||
mantissa = i + 1;
|
||
break;
|
||
}
|
||
}
|
||
exponent = bits - sign - mantissa;
|
||
|
||
// printf("---------------- %d\r\n", bits);
|
||
// printf("mantissa %d\r\n", mantissa);
|
||
// printf("exponent %d\r\n", exponent);
|
||
// printf("sign %d\r\n", sign);
|
||
|
||
// printf("high %d\r\n", mantissa & 0x1F); // % 32
|
||
// printf("part %d\r\n", mantissa >> 5); // / 32
|
||
|
||
if (exponent >= 31) return 0;
|
||
|
||
return exponent;
|
||
}
|
||
|
||
void floatl_cfg_const_out(FILE* file, FLINFO *info)
|
||
{
|
||
if (!file || !info) return;
|
||
|
||
fprintf(file, "(floatl){.u32={");
|
||
for (int i = 0; i < info->FLOATL_U32_PARTS__; i++)
|
||
{
|
||
uint32_t value = info->buffer[i];
|
||
if (value == 0) fprintf(file, "0,");
|
||
else if (value == 0xFFFFFFFF) fprintf(file, "-1,");
|
||
else fprintf(file, "0x%08X,", value);
|
||
}
|
||
fprintf(file, "}}\n");
|
||
}
|
||
|
||
int floatl_cfg_info_init(FLINFO *info, unsigned int bits)
|
||
{
|
||
if ((bits & (bits - 1)) != 0) return 0;
|
||
|
||
info->FLOATL_BIT_PARTS__ = bits;
|
||
info->FLOATL_U32_PARTS__ = info->FLOATL_BIT_PARTS__ >> (5);
|
||
info->FLOATL_U16_PARTS__ = info->FLOATL_BIT_PARTS__ >> (4);
|
||
info->FLOATL_EXP_BITS__ = floatl_cfg_gen_exp(info->FLOATL_BIT_PARTS__);
|
||
info->FLOATL_MANT_BITS__ = info->FLOATL_BIT_PARTS__ - info->FLOATL_EXP_BITS__ - 1;
|
||
info->FLOATL_MANT_PARTS__ = info->FLOATL_MANT_BITS__ >> 5;
|
||
info->FLOATL_MANT_HIGH_BITS__ = info->FLOATL_MANT_BITS__ & 0x1F;
|
||
info->FLOATL_EXP_MID_VALUE__ = (uint32_t)pow(2, info->FLOATL_EXP_BITS__ - 1) - 1;
|
||
info->FLOATL_EXP_WHL_VALUE__ = (uint32_t)pow(2, info->FLOATL_EXP_BITS__) - 1;
|
||
info->FLOATL2_BIT_PARTS__ = info->FLOATL_BIT_PARTS__ * 2;
|
||
info->FLOATL2_U32_PARTS__ = info->FLOATL2_BIT_PARTS__ >> (5);
|
||
info->FLOATL2_U16_PARTS__ = info->FLOATL2_BIT_PARTS__ >> (4);
|
||
info->FLOATL_MANT_DIG__ = info->FLOATL_MANT_BITS__ + 1;
|
||
info->FLOATL_MIN_EXP__ = - info->FLOATL_EXP_MID_VALUE__ + 2;
|
||
info->FLOATL_MAX_EXP__ = info->FLOATL_EXP_MID_VALUE__ + 1;
|
||
info->FLOATL_DIG__ = floor(log10(2) * info->FLOATL_MANT_DIG__);
|
||
info->FLOATL_DECIMAL_DIG__ = ceil(log10(2) * info->FLOATL_MANT_DIG__) + 1;
|
||
info->FLOATL_MIN_10_EXP__ = ceil(log10(2) * (int32_t)(1 - info->FLOATL_EXP_MID_VALUE__));
|
||
info->FLOATL_MAX_10_EXP__ = - info->FLOATL_MIN_10_EXP__ + 1;
|
||
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
return 1;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_set_sign(FLINFO *info, int sign)
|
||
{
|
||
if (sign) info->buffer[info->FLOATL_U32_PARTS__ - 1] |= (1 << 31);
|
||
else info->buffer[info->FLOATL_U32_PARTS__ - 1] &= ~(1 << 31);
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_set_exp_real(FLINFO *info, uint32_t t)
|
||
{
|
||
uint32_t temp = 1;
|
||
|
||
temp <<= info->FLOATL_EXP_BITS__;
|
||
temp -= 1;
|
||
|
||
t &= temp;
|
||
t <<= (32 - info->FLOATL_EXP_BITS__ - 1);
|
||
|
||
temp <<= (32 - info->FLOATL_EXP_BITS__ - 1);
|
||
|
||
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~temp); // clear
|
||
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] |= t;
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_set_exp(FLINFO *info, int32_t e)
|
||
{
|
||
return floatl_cfg_set_exp_real(info, e + info->FLOATL_EXP_MID_VALUE__);
|
||
}
|
||
|
||
FLINFO *floatl_cfg_set_mant(FLINFO *info, char *bin)
|
||
{
|
||
char *s = bin;
|
||
|
||
while (*s)
|
||
{
|
||
int index = info->FLOATL_MANT_BITS__ - (s - bin) - 1;
|
||
if (index < 0) break;
|
||
if (index >= 0 && *s == '1')
|
||
{
|
||
int part = index / 32;
|
||
int bit = index % 32;
|
||
|
||
info->buffer[part] |= (1 << bit);
|
||
}
|
||
s++;
|
||
}
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_int_zero(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_const_0(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_const_1(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] &= 0xBFFFFFFF;
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_const_1eX(FLINFO *info, uint32_t e)
|
||
{
|
||
if (e >= sizeof(fle_list) / sizeof(fle_list[0])) return NULL;
|
||
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp(info, fle_list[e].exp);
|
||
floatl_cfg_set_mant(info, fle_list[e].mantBin);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_const_pi(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp(info, 1);
|
||
floatl_cfg_set_mant(info, FLOATL_CFG_MANT_PI);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_const_e(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp(info, 1);
|
||
floatl_cfg_set_mant(info, FLOATL_CFG_MANT_E);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_inf(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_nan(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] |= 0x80000000;
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_all_sign(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] |= 0x80000000;
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_all_exp(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_all_mant(FLINFO *info)
|
||
{
|
||
memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 0);
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_all_exp_mant(FLINFO *info)
|
||
{
|
||
memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_hide_mant_bit(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 1);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_mant_plus_max(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 8);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_mant_whole(FLINFO *info)
|
||
{
|
||
memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 1);
|
||
info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_min(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_exp_real(info, 1);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_max(FLINFO *info)
|
||
{
|
||
memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
floatl_cfg_set_sign(info, 0);
|
||
floatl_cfg_set_exp_real(info, 0xFFFFFFFE);
|
||
|
||
return info;
|
||
}
|
||
|
||
FLINFO *floatl_cfg_epsilon(FLINFO *info)
|
||
{
|
||
memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));
|
||
|
||
info->buffer[0] |= 1;
|
||
|
||
return info;
|
||
}
|
||
|
||
void floatl_cfg_generate(uint32_t bits, const char *filename)
|
||
{
|
||
#define MINBITS 64
|
||
#define NEWLINE "\n" // "\r\n" //
|
||
|
||
FILE* output = stdout; // Default output to standard output
|
||
if (filename != NULL)
|
||
{
|
||
output = fopen(filename, "w");
|
||
if (output == NULL)
|
||
{
|
||
fprintf(stdout, "[ERROR] Failed to open output file"NEWLINE);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* The number of bits in an floatl needs to be an exponent of two */
|
||
if ((bits & (bits - 1)) != 0)
|
||
{
|
||
fprintf(stdout, "[ERROR] `bits` not a power of 2"NEWLINE);
|
||
return;
|
||
}
|
||
|
||
/* Enable PI and E mant configurations */
|
||
if (!FLOATL_CFG_MANT_PI || !FLOATL_CFG_MANT_E)
|
||
{
|
||
fprintf(stdout, "[ERROR] Enable `FLOATL_CFG_MANT_PI` and `FLOATL_CFG_MANT_E` in `floatl_cfg.h` file"NEWLINE);
|
||
return;
|
||
}
|
||
|
||
/* Limiting the minimum bit */
|
||
if (bits < MINBITS)
|
||
{
|
||
fprintf(stdout, "[ERROR] `bits` too small\r\b");
|
||
return;
|
||
}
|
||
|
||
fprintf(output, "/*****************************************************************/"NEWLINE);
|
||
fprintf(output, "/* Config start */"NEWLINE);
|
||
fprintf(output, "/*****************************************************************/"NEWLINE);
|
||
|
||
for (uint32_t tb = MINBITS; tb <= bits; tb <<= 1)
|
||
{
|
||
if (tb == MINBITS)
|
||
{
|
||
fprintf(output, "#define FLOATL_USE_%uBITS"NEWLINE, tb);
|
||
}
|
||
else
|
||
{
|
||
fprintf(output, "// #define FLOATL_USE_%uBITS"NEWLINE, tb);
|
||
}
|
||
}
|
||
fprintf(output, NEWLINE);
|
||
|
||
floatl temp;
|
||
|
||
for (uint32_t tb = MINBITS; tb <= bits; tb <<= 1)
|
||
{
|
||
if (tb == MINBITS)
|
||
{
|
||
fprintf(output, "#if defined(FLOATL_USE_%uBITS)"NEWLINE, tb);
|
||
}
|
||
else
|
||
{
|
||
fprintf(output, "#elif defined(FLOATL_USE_%uBITS)"NEWLINE, tb);
|
||
}
|
||
|
||
FLINFO info;
|
||
floatl_cfg_info_init(&info, tb);
|
||
|
||
fprintf(output, "#define __FLOATL_BIT_PARTS__ %u"NEWLINE, info.FLOATL_BIT_PARTS__ );
|
||
fprintf(output, "#define __FLOATL_U32_PARTS__ %u"NEWLINE, info.FLOATL_U32_PARTS__ );
|
||
fprintf(output, "#define __FLOATL_U16_PARTS__ %u"NEWLINE, info.FLOATL_U16_PARTS__ );
|
||
fprintf(output, "#define __FLOATL_EXP_BITS__ %u"NEWLINE, info.FLOATL_EXP_BITS__ );
|
||
fprintf(output, "#define __FLOATL_MANT_BITS__ %u"NEWLINE, info.FLOATL_MANT_BITS__ );
|
||
fprintf(output, "#define __FLOATL_MANT_PARTS__ %u"NEWLINE, info.FLOATL_MANT_PARTS__ );
|
||
fprintf(output, "#define __FLOATL_MANT_HIGH_BITS__ %u"NEWLINE, info.FLOATL_MANT_HIGH_BITS__ );
|
||
fprintf(output, "#define __FLOATL_EXP_MID_VALUE__ %u"NEWLINE, info.FLOATL_EXP_MID_VALUE__ );
|
||
fprintf(output, "#define __FLOATL_EXP_WHL_VALUE__ %u"NEWLINE, info.FLOATL_EXP_WHL_VALUE__ );
|
||
|
||
fprintf(output, "#define __FLOATL2_BIT_PARTS__ %u"NEWLINE, info.FLOATL2_BIT_PARTS__ );
|
||
fprintf(output, "#define __FLOATL2_U32_PARTS__ %u"NEWLINE, info.FLOATL2_U32_PARTS__ );
|
||
fprintf(output, "#define __FLOATL2_U16_PARTS__ %u"NEWLINE, info.FLOATL2_U16_PARTS__ );
|
||
|
||
fprintf(output, "#define __FLOATL_MANT_DIG__ %u"NEWLINE, info.FLOATL_MANT_DIG__ );
|
||
fprintf(output, "#define __FLOATL_MIN_EXP__ %d"NEWLINE, info.FLOATL_MIN_EXP__ );
|
||
fprintf(output, "#define __FLOATL_MAX_EXP__ %u"NEWLINE, info.FLOATL_MAX_EXP__ );
|
||
fprintf(output, "#define __FLOATL_DIG__ %u"NEWLINE, info.FLOATL_DIG__ );
|
||
fprintf(output, "#define __FLOATL_DECIMAL_DIG__ %u"NEWLINE, info.FLOATL_DECIMAL_DIG__ );
|
||
fprintf(output, "#define __FLOATL_MIN_10_EXP__ %d"NEWLINE, info.FLOATL_MIN_10_EXP__ );
|
||
fprintf(output, "#define __FLOATL_MAX_10_EXP__ %u"NEWLINE, info.FLOATL_MAX_10_EXP__ );
|
||
|
||
fprintf(output, "#define __FLOATL_CONST_0__ "); floatl_cfg_const_out(output, floatl_cfg_const_0(&info) );
|
||
fprintf(output, "#define __FLOATL_CONST_1__ "); floatl_cfg_const_out(output, floatl_cfg_const_1(&info) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e0__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,0) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e1__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,1) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e2__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,2) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e3__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,3) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e4__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,4) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e5__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,5) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e6__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,6) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e7__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,7) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e8__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,8) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e9__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,9) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e10__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,10) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e11__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,11) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e12__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,12) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e13__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,13) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e14__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,14) );
|
||
fprintf(output, "#define __FLOATL_CONST_1e15__ "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,15) );
|
||
fprintf(output, "#define __FLOATL_CONST_PI__ "); floatl_cfg_const_out(output, floatl_cfg_const_pi(&info) );
|
||
fprintf(output, "#define __FLOATL_CONST_E__ "); floatl_cfg_const_out(output, floatl_cfg_const_e(&info) );
|
||
fprintf(output, "#define __FLOATL_INT_ZERO__ "); floatl_cfg_const_out(output, floatl_cfg_int_zero(&info) );
|
||
fprintf(output, "#define __FLOATL_INF__ "); floatl_cfg_const_out(output, floatl_cfg_inf(&info) );
|
||
fprintf(output, "#define __FLOATL_NAN__ "); floatl_cfg_const_out(output, floatl_cfg_nan(&info) );
|
||
fprintf(output, "#define __FLOATL_ALL_SIGN__ "); floatl_cfg_const_out(output, floatl_cfg_all_sign(&info) );
|
||
fprintf(output, "#define __FLOATL_ALL_EXP__ "); floatl_cfg_const_out(output, floatl_cfg_all_exp(&info) );
|
||
fprintf(output, "#define __FLOATL_ALL_MANT__ "); floatl_cfg_const_out(output, floatl_cfg_all_mant(&info) );
|
||
fprintf(output, "#define __FLOATL_ALL_EXP_MANT__ "); floatl_cfg_const_out(output, floatl_cfg_all_exp_mant(&info) );
|
||
fprintf(output, "#define __FLOATL_HIDE_MANT_BIT__ "); floatl_cfg_const_out(output, floatl_cfg_hide_mant_bit(&info) );
|
||
fprintf(output, "#define __FLOATL_MANT_PLUS_MAX__ "); floatl_cfg_const_out(output, floatl_cfg_mant_plus_max(&info) );
|
||
fprintf(output, "#define __FLOATL_MANT_WHL__ "); floatl_cfg_const_out(output, floatl_cfg_mant_whole(&info) );
|
||
fprintf(output, "#define __FLOATL_MIN__ "); floatl_cfg_const_out(output, floatl_cfg_min(&info) );
|
||
fprintf(output, "#define __FLOATL_MAX__ "); floatl_cfg_const_out(output, floatl_cfg_max(&info) );
|
||
fprintf(output, "#define __FLOATL_EPSILON__ "); floatl_cfg_const_out(output, floatl_cfg_epsilon(&info) );
|
||
}
|
||
|
||
fprintf(output, "#endif"NEWLINE);
|
||
|
||
fprintf(output, "/*****************************************************************/"NEWLINE);
|
||
fprintf(output, "/* Config end */"NEWLINE);
|
||
fprintf(output, "/*****************************************************************/"NEWLINE);
|
||
|
||
printf("\r\n\r\n-------------------------------------------------------------\r\n\r\n");
|
||
|
||
if (sizeof(temp) != (bits >> 3))
|
||
{
|
||
printf("[TODO] Apply the current configuration and run it again to get the new `FLOATL_MAX_DEC`\r\n");
|
||
}
|
||
else
|
||
{
|
||
printf("[INFO] The configuration has been generated, copy it to the `floatl_cfg.h` range specified\r\n");
|
||
}
|
||
}
|
||
|
||
/*----------------------------------------------------------------------------------*/
|
||
/* Generate configuration [END] */
|
||
/*----------------------------------------------------------------------------------*/
|
||
|
||
static void test_error()
|
||
{
|
||
double d1 = 123.456;
|
||
double d2 = 999.365;
|
||
double d3 = d1 + d2;
|
||
|
||
floatl f1 = floatl(d1);
|
||
floatl f2 = floatl(d2);
|
||
floatl f3 = floatl_add(f1, f2);
|
||
|
||
printf("----------------\r\n");
|
||
double a = 0x8.153800p-134;
|
||
printf("a: %a\r\n", a); // print 0x1.02a700p-131
|
||
snprintf(buffer, sizeof(buffer), "%a", a);
|
||
printf("b: %s\r\n", buffer); // print 0x8.1538p-134
|
||
// why?
|
||
|
||
// // 1234567890
|
||
// floatl a = floatl_from("0x1.26580B48p+30"); // 1 001001100101100000001011010010 00
|
||
|
||
// floatl a = FLOATL_CONST_0;
|
||
|
||
// FLINFO info;
|
||
// floatl_cfg_info_init(&info, sizeof(floatl) * 8);
|
||
|
||
// 1234567890
|
||
// 1 001001100101100000001011010010 00
|
||
// floatl_cfg_set_exp(&info, 30);
|
||
// floatl_cfg_set_mant(&info, "001001100101100000001011010010");
|
||
|
||
// 123456789012345678901234567890
|
||
// 1 100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010
|
||
// floatl_cfg_set_exp(&info, 96);
|
||
// floatl_cfg_set_mant(&info, "100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010");
|
||
|
||
// a = *((floatl *)(info.buffer));
|
||
|
||
// double_print(stdout, d1);
|
||
// printf("double_from_string %f\r\n", double_from_string("123.456"));
|
||
// printf("double_from_string %e\r\n", double_from_string("1.23456e+2"));
|
||
// printf("double_from_string %a\r\n", double_from_string("0x1.23456p+2"));
|
||
|
||
printf("%020a\r\n", d1);
|
||
printf("%s\r\n", floatl_show(f1, buffer, sizeof(buffer), "020a"));
|
||
printf("from %s\r\n", floatl_show(floatl_from("123.456"), buffer, sizeof(buffer), "%f"));
|
||
printf("from %s\r\n", floatl_show(floatl_from("1.23456e+2"), buffer, sizeof(buffer), "%e"));
|
||
|
||
printf("------------\r\n");
|
||
d1 = 0x1.43a2073p+1;
|
||
printf("%020e\r\n", d1);
|
||
printf("%s\r\n", floatl_show(floatl_from("0x1.43a2073p+1"), buffer, sizeof(buffer), "%020e"));
|
||
|
||
if (memcmp(&d3, &f3, sizeof(d3)))
|
||
{
|
||
double_print(stdout, d1);
|
||
double_print(stdout, d2);
|
||
double_print(stdout, d3);
|
||
double_print(stdout, *(double *)(&f3));
|
||
}
|
||
}
|
||
|
||
static void test_define(void)
|
||
{
|
||
floatl a = floatl(0.0);
|
||
floatl b = floatl(10.0);
|
||
floatl c = floatl(-3.14);
|
||
floatl d = floatl(1.23456789e+10);
|
||
floatl e = floatl(0x1.23456789p+10);
|
||
|
||
floatl f = floatl_from("0.0");
|
||
floatl g = floatl_from("10.0");
|
||
floatl h = floatl_from("-3.14");
|
||
floatl i = floatl_from("1.23456789e+10");
|
||
floatl j = floatl_from("0x1.23456789p+10");
|
||
|
||
floatl zero = FLOATL_CONST_0;
|
||
floatl one = FLOATL_CONST_1;
|
||
floatl ten = FLOATL_CONST_10;
|
||
|
||
printf("size %d\r\n", sizeof(floatl));
|
||
}
|
||
|
||
static void test_print(void)
|
||
{
|
||
floatl a = floatl_from("1234567890.123456789");
|
||
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%f"));
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%.2f"));
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%020.2f"));
|
||
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%+.6a"));
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%+20.6a"));
|
||
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%-20.6e"));
|
||
}
|
||
|
||
static void test_calculate(void)
|
||
{
|
||
floatl a = floatl_from("12345678901234567890.123456789");
|
||
floatl b = floatl_from("98765432109876543210.987654321");
|
||
|
||
printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%f"));
|
||
printf("b %s\r\n", floatl_show(b, buffer, sizeof(buffer), "%f"));
|
||
|
||
printf("a + b: %s\r\n", floatl_show(floatl_add(a, b), buffer, sizeof(buffer), "%f"));
|
||
printf("a - b: %s\r\n", floatl_show(floatl_sub(a, b), buffer, sizeof(buffer), "%f"));
|
||
printf("a * b: %s\r\n", floatl_show(floatl_mul(a, b), buffer, sizeof(buffer), "%f"));
|
||
printf("a / b: %s\r\n", floatl_show(floatl_div(a, b), buffer, sizeof(buffer), "%f"));
|
||
printf("-a: %s\r\n", floatl_show(floatl_neg(a), buffer, sizeof(buffer), "%f"));
|
||
printf("|a|: %s\r\n", floatl_show(floatl_abs(a), buffer, sizeof(buffer), "%f"));
|
||
printf("a > b: %d\r\n", floatl_gt(a, b));
|
||
printf("a >= b: %d\r\n", floatl_ge(a, b));
|
||
printf("a < b: %d\r\n", floatl_lt(a, b));
|
||
printf("a <= b: %d\r\n", floatl_le(a, b));
|
||
printf("a == b: %d\r\n", floatl_eq(a, b));
|
||
printf("a != b: %d\r\n", floatl_ne(a, b));
|
||
}
|
||
|
||
static void test_base(void)
|
||
{
|
||
float s1 = 12.625;
|
||
float s2 = 16.456;
|
||
double d1 = 12.625;
|
||
double d2 = 16.456;
|
||
floatl f1 = floatl_from_d(d1);
|
||
floatl f2 = floatl_from_d(d2);
|
||
|
||
float s;
|
||
double d;
|
||
floatl f;
|
||
float temp;
|
||
|
||
printf("==============================================\r\n");
|
||
printf("==================== + =======================\r\n");
|
||
printf("==============================================\r\n");
|
||
|
||
s = s1 + s2;
|
||
d = d1 + d2;
|
||
f = floatl_add(f1, f2);
|
||
|
||
float_show_raw(s);
|
||
double_show_raw(d);
|
||
floatl_show_raw(f, 0, 0, 0);
|
||
|
||
printf("==============================================\r\n");
|
||
printf("==================== - =======================\r\n");
|
||
printf("==============================================\r\n");
|
||
|
||
s = s1 - s2;
|
||
d = d1 - d2;
|
||
f = floatl_sub(f1, f2);
|
||
|
||
float_show_raw(s);
|
||
double_show_raw(d);
|
||
floatl_show_raw(f, 0, 0, 0);
|
||
|
||
printf("==============================================\r\n");
|
||
printf("==================== * =======================\r\n");
|
||
printf("==============================================\r\n");
|
||
|
||
s = s1 * s2;
|
||
d = d1 * d2;
|
||
f = floatl_mul(f1, f2);
|
||
|
||
float_show_raw(s);
|
||
double_show_raw(d);
|
||
floatl_show_raw(f, 0, 0, 0);
|
||
|
||
printf("==============================================\r\n");
|
||
printf("==================== / =======================\r\n");
|
||
printf("==============================================\r\n");
|
||
|
||
s = s1 / s2;
|
||
d = d1 / d2;
|
||
f = floatl_div(f1, f2);
|
||
|
||
float_show_raw(s);
|
||
double_show_raw(d);
|
||
floatl_show_raw(f, 0, 0, 0);
|
||
|
||
// float l1 = 1.0f;
|
||
// showBits(&l1, sizeof(l1));
|
||
|
||
#if 0
|
||
// float_show_raw(0.0f);
|
||
// float_show_raw(1.0f);
|
||
|
||
// double y = 11.6250;
|
||
// int e = 0;
|
||
// double_show_raw(y);
|
||
// y = frexp(y, &e);
|
||
// double_show_raw(y);
|
||
// printf("y %f e %d\r\n", y, e);
|
||
#endif
|
||
double v = -3.14;
|
||
printf("ceil %f\r\n", ceil(v));
|
||
printf("floor %f\r\n", floor(v));
|
||
printf("round %f\r\n", round(v));
|
||
|
||
printf("double_ceil %f\r\n", double_ceil(v));
|
||
printf("double_floor %f\r\n", double_floor(v));
|
||
printf("double_round %f\r\n", double_round(v));
|
||
|
||
printf("floatl_ceil %s\r\n", floatl_show(floatl_ceil(floatl(v)), buffer, sizeof(buffer), "%f"));
|
||
printf("floatl_floor %s\r\n", floatl_show(floatl_floor(floatl(v)), buffer, sizeof(buffer), "%f"));
|
||
printf("floatl_round %s\r\n", floatl_show(floatl_round(floatl(v)), buffer, sizeof(buffer), "%f"));
|
||
|
||
printf("to_d %f\r\n", floatl_to_d(FLOATL_PI));
|
||
}
|
||
|
||
/************************************************************************************/
|
||
/************************************* Command ************************************/
|
||
/************************************************************************************/
|
||
|
||
static void usage(void)
|
||
{
|
||
printf(
|
||
"Usage: floatl [opt] [arg] ...\n"
|
||
"\n"
|
||
"options:\n"
|
||
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
|
||
" <base> Test base function\n"
|
||
" <ut> Unit test\n"
|
||
" <define> Test define function\n"
|
||
" <cal> Calculate string math expression\n"
|
||
" <gen> Generate the floatl configuration file code segment, need specify -f -b\n"
|
||
" <print> Print an floatl number\n"
|
||
" <error> Function that tests for floatl errors\n"
|
||
" -l <format> Format string, 10d, 08x, ...\n"
|
||
" -o <op function> Operate function, add, sub, mul, div, ...\n"
|
||
" -n <floatl> floatl number expression\n"
|
||
" -f <filename> File name, temporarily store configuration code segment\n"
|
||
" -b <bits> Maximum number of configured bits\n"
|
||
" -h Print help\n"
|
||
" -v Print version\n"
|
||
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
|
||
"\n"
|
||
|
||
);
|
||
}
|
||
|
||
static int test(int argc, char *argv[])
|
||
{
|
||
char *execute = NULL;
|
||
int ut_period = 1000;
|
||
char *filename = NULL;
|
||
int bits = 0;
|
||
|
||
/* reset getopt */
|
||
command_opt_init();
|
||
|
||
while (1)
|
||
{
|
||
int opt = command_getopt(argc, argv, "e:hvu::f:b:n:o:l:");
|
||
if (opt == -1) break;
|
||
|
||
switch (opt)
|
||
{
|
||
case 'b' :
|
||
bits = atoi(command_optarg);
|
||
break;
|
||
case 'f' :
|
||
filename = command_optarg;
|
||
break;
|
||
case 'u' :
|
||
if (command_optarg) ut_period = atoi(command_optarg);
|
||
break;
|
||
case 'e' :
|
||
execute = command_optarg;
|
||
break;
|
||
case 'v' :
|
||
printf("floatl version %d.%d.%d\r\n", FLOATL_V_MAJOR, FLOATL_V_MINOR, FLOATL_V_PATCH);
|
||
return 0;
|
||
case '?':
|
||
printf("Unknown option `%c`\r\n", command_optopt);
|
||
return -1;
|
||
case 'h' :
|
||
default:
|
||
usage();
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (execute)
|
||
{
|
||
if (!strcmp(execute, "base"))
|
||
{
|
||
test_base();
|
||
}
|
||
else if (!strcmp(execute, "ut"))
|
||
{
|
||
srand((uint32_t)time(NULL));
|
||
#if defined(TEST_TARGET_floatl)
|
||
while (1)
|
||
{
|
||
unitt_task();
|
||
usleep(1000 * ut_period);
|
||
}
|
||
#else
|
||
printf("create task %d\r\n", task_create(ut_period, unitt_task));
|
||
#endif
|
||
}
|
||
else if (!strcmp(execute, "define"))
|
||
{
|
||
test_define();
|
||
}
|
||
else if (!strcmp(execute, "gen"))
|
||
{
|
||
floatl_cfg_generate(bits, filename);
|
||
}
|
||
else if (!strcmp(execute, "cal"))
|
||
{
|
||
test_calculate();
|
||
}
|
||
else if (!strcmp(execute, "print"))
|
||
{
|
||
test_print();
|
||
}
|
||
else if (!strcmp(execute, "error"))
|
||
{
|
||
test_error();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
test_base();
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/************************************************************************************/
|
||
/************************************ Test entry ************************************/
|
||
/************************************************************************************/
|
||
|
||
#if defined(TEST_TARGET_floatl)
|
||
int main(int argc, char *argv[])
|
||
{
|
||
return test(argc, argv);
|
||
}
|
||
#else
|
||
void test_floatl(void)
|
||
{
|
||
command_export("floatl", test);
|
||
}
|
||
init_export_app(test_floatl);
|
||
#endif
|