/* ***************************************************************************** * libg1m/bcd.h -- BCD-encoded numbers manipulation. * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey * * This file is part of libg1m. * libg1m is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3.0 of the License, * or (at your option) any later version. * * libg1m is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libg1m; if not, see . * * BCD are the main number format CASIO uses in its calculators. * This format has the huge advantage to make 0.1 + 0.2 and 0.3 equal. * * There are several raw BCD formats, libg1m offers conversion to its own * format, easier to read and write (but not optimized for storing). * ************************************************************************** */ #ifndef LIBG1M_BCD_H # define LIBG1M_BCD_H # ifdef __cplusplus extern "C" { # endif # define G1M_BCD_MANTISSA_SIZE 16 # define G1M_BCD_EXPMIN -99 # define G1M_BCD_EXPMAX 99 /* String constants; here are some examples: * - 1.23456789ABCDEFe99 * - -0.0000123456789ABCDEF * - 123456789ABCDEF0000000 */ # define G1M_BCD_GOODBUFSIZE \ (2 + G1M_BCD_MANTISSA_SIZE + 4 + 2 + 1) /* ************************************************************************** */ /* Main type */ /* ************************************************************************** */ /* libg1m type definition * This is the "unzipped" BCD format. There are several, this is the one libg1m * will always convert to. * * `flags` is the flags (negative, special, precision) of the BCD. * `exp` is the 10-exponent, between -99 and 99. * `mant` is the mantissa: unpacked BCD digits (one digit per byte). * * The first digit of the mantissa is the integer part, the others are * the decimal part. * * To interact the flags or the exponent, it is advised to use the g1m_bcd_* * macros, as some of them could change. */ # define g1m_bcdflag_spe 0x80 # define g1m_bcdflag_neg 0x40 # define g1m_bcdmask_pre 0x3F # define g1m_bcd_has_special(B) ((B)->flags & g1m_bcdflag_spe) # define g1m_bcd_is_negative(B) ((B)->flags & g1m_bcdflag_neg) # define g1m_bcd_precision(B) ((B)->flags & g1m_bcdmask_pre) # define g1m_bcd_exponent(B) ((B)->exp) # define g1m_make_bcdflags(SPE, NEG, PREC) \ (((SPE) << 7) | ((NEG) << 6) | (PREC)) typedef struct g1m_bcd_s { unsigned char flags; char exp; char mant[G1M_BCD_MANTISSA_SIZE]; } g1m_bcd_t; /* ************************************************************************** */ /* Raw formats */ /* ************************************************************************** */ /* CAS BCD -- the old BCD format. * * First two nibbles are the integer part, the fourteen that follow are * the decimal part. They are followed by the sign info byte, then with a * BCD exponent (two nibbles). * * The sign info byte is basically the flags of the number. * The negative is two bits/flags (I don't know why). * `g1m_casbcd_pow_neg`, if there, means the power is negative. */ # define g1m_casbcd_special 0x80 # define g1m_casbcd_negative 0x50 # define g1m_casbcd_pow_neg 0x01 typedef struct { unsigned char mantissa[8]; unsigned char signinfo; unsigned char exponent; } cas_bcd_t; /* MCS BCD -- the most recent BCD format. * Only the first 9 bytes are significant. * * The highest bit of the first byte isn't to be considered as part of the * first nibble, it means something else (generally "the number has a complex * part"). * * The first three nibbles are the exponent. If it is more than 500, the * number is negative. Its offset is -99. * The other values (from the fourth nibble) are the packed BCD mantissa. * It starts at 10^0. */ typedef struct bcd { unsigned char BCDval[9]; unsigned char _align[3]; } mcs_bcd_t; /* ************************************************************************** */ /* Conversion utilities */ /* ************************************************************************** */ /* From and to MCS BCD. */ int g1m_bcd_frommcs(const mcs_bcd_t *raw, g1m_bcd_t *bcd); void g1m_bcd_tomcs(const g1m_bcd_t *bcd, mcs_bcd_t *raw); /* From and to CAS BCD. */ int g1m_bcd_fromcas(const cas_bcd_t *raw, g1m_bcd_t *bcd); void g1m_bcd_tocas(const g1m_bcd_t *bcd, cas_bcd_t *raw); /* From and to C-double */ void g1m_bcd_fromdouble(double dbl, g1m_bcd_t *bcd); double g1m_bcd_todouble(const g1m_bcd_t *bcd); /* Make a string out of a BCD */ size_t g1m_bcdtoa(const g1m_bcd_t *bcd, char *buf, size_t len); # ifdef __cplusplus } # endif #endif /* LIBG1M_BCD_H */