460 lines
13 KiB
C
460 lines
13 KiB
C
// Copyright (c) 2015-2019 The HomeKit ADK Contributors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the “License”);
|
|
// you may not use this file except in compliance with the License.
|
|
// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors.
|
|
|
|
#include "HAPPlatform.h"
|
|
|
|
HAP_RESULT_USE_CHECK
|
|
float HAPFloatFromBitPattern(uint32_t bitPattern) {
|
|
float value;
|
|
HAPAssert(sizeof value == sizeof bitPattern);
|
|
HAPRawBufferCopyBytes(&value, &bitPattern, sizeof value);
|
|
return value;
|
|
}
|
|
|
|
HAP_RESULT_USE_CHECK
|
|
uint32_t HAPFloatGetBitPattern(float value) {
|
|
uint32_t bitPattern;
|
|
HAPAssert(sizeof bitPattern == sizeof value);
|
|
HAPRawBufferCopyBytes(&bitPattern, &value, sizeof bitPattern);
|
|
return bitPattern;
|
|
}
|
|
|
|
//----------------------------- Bigint Implementation ------------------------------
|
|
|
|
#define kInt_NumberOfWords (6) // Number of words (total 168 bits).
|
|
#define kInt_BitsPerWord (28) // Bits per word.
|
|
#define kInt_BitMask ((1 << kInt_BitsPerWord) - 1)
|
|
|
|
typedef struct {
|
|
uint32_t w[kInt_NumberOfWords];
|
|
uint32_t len;
|
|
} Bigint;
|
|
|
|
// x = val
|
|
static void BigintInit(Bigint* x, uint64_t value) {
|
|
uint32_t n = 0;
|
|
while (value) {
|
|
x->w[n] = (uint32_t) value & kInt_BitMask;
|
|
value >>= kInt_BitsPerWord;
|
|
n++;
|
|
}
|
|
x->len = n;
|
|
}
|
|
|
|
// Returns 0 if x == y, <0 if x < y, >0 if x > y
|
|
static int32_t BigintComp(const Bigint* x, const Bigint* y) {
|
|
uint32_t nx = x->len, ny = y->len;
|
|
int32_t delta = (int32_t) nx - (int32_t) ny;
|
|
if (delta)
|
|
return delta;
|
|
while (nx > 0) {
|
|
nx--;
|
|
delta = (int32_t) x->w[nx] - (int32_t) y->w[nx];
|
|
if (delta)
|
|
return delta;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// z = x + y
|
|
static void BigintAdd(const Bigint* x, const Bigint* y, Bigint* z) {
|
|
uint32_t c = 0, i = 0, nx = x->len, ny = y->len;
|
|
while (i < nx || i < ny || c != 0) {
|
|
c += (i < nx ? x->w[i] : 0) + (i < ny ? y->w[i] : 0);
|
|
z->w[i] = c & kInt_BitMask;
|
|
c >>= kInt_BitsPerWord;
|
|
i++;
|
|
}
|
|
z->len = i;
|
|
}
|
|
|
|
// x = x * n, 2 <= n <= 10
|
|
static void BigintMul(Bigint* x, uint32_t n) {
|
|
uint32_t c = 0, i = 0, nx = x->len;
|
|
while (i < nx) {
|
|
c += x->w[i] * n;
|
|
x->w[i] = c & kInt_BitMask;
|
|
c >>= kInt_BitsPerWord;
|
|
i++;
|
|
}
|
|
if (c) {
|
|
x->w[i] = c;
|
|
x->len = i + 1;
|
|
}
|
|
}
|
|
|
|
// x = x % y; returns x / y
|
|
// pre: x < 10 * y
|
|
static uint32_t BigintDivRem(Bigint* x, const Bigint* y) {
|
|
uint32_t q = 0, ny = y->len;
|
|
while (BigintComp(x, y) >= 0) {
|
|
uint32_t i = 0, nx = x->len, n = 0;
|
|
int32_t c = 0;
|
|
q++;
|
|
while (i < nx) {
|
|
// x = x - y
|
|
c += x->w[i];
|
|
if (i < ny)
|
|
c -= y->w[i];
|
|
x->w[i] = c & kInt_BitMask;
|
|
i++;
|
|
if (c != 0)
|
|
n = i; // Remember most significant word.
|
|
c >>= kInt_BitsPerWord;
|
|
}
|
|
x->len = n;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
HAP_RESULT_USE_CHECK
|
|
HAPError HAPFloatFromString(const char* string, float* value) {
|
|
HAPPrecondition(string);
|
|
HAPPrecondition(value);
|
|
|
|
// - We don't want to accept leading or trailing whitespace.
|
|
// - We don't want to accept hexadecimal floats for now.
|
|
// - We don't want to accept infinity / nan for now.
|
|
// - We only want to accept standalone values.
|
|
*value = 0.0F;
|
|
char c = string[0];
|
|
int i = 1;
|
|
|
|
// Read sign.
|
|
uint32_t sign = 0;
|
|
if (c == '-') {
|
|
sign = 0x80000000;
|
|
c = string[i++];
|
|
} else if (c == '+') {
|
|
c = string[i++];
|
|
}
|
|
|
|
// Read mantissa.
|
|
uint64_t mant = 0;
|
|
int dp = 0;
|
|
int digits = 0;
|
|
int exp10 = 0; // Base 10 exponent.
|
|
for (;;) {
|
|
if (c == '.' && !dp) {
|
|
dp = 1;
|
|
} else if (c >= '0' && c <= '9') {
|
|
if (!dp)
|
|
exp10++;
|
|
if (mant < 100000000000000000ll) { // 10^17
|
|
mant = mant * 10 + (uint64_t)(c - '0');
|
|
exp10--;
|
|
}
|
|
digits++;
|
|
} else {
|
|
break;
|
|
}
|
|
c = string[i++];
|
|
}
|
|
if (digits == 0) {
|
|
// No mantissa digits.
|
|
return kHAPError_InvalidData;
|
|
}
|
|
/* mantissa == mant * 10^exp10, mant < 10^18 */
|
|
|
|
// Read exponent.
|
|
if (c == 'e' || c == 'E') {
|
|
// Scan exponent.
|
|
c = string[i++];
|
|
int expSign = 1;
|
|
if (c == '-') {
|
|
expSign = -1;
|
|
c = string[i++];
|
|
} else if (c == '+') {
|
|
c = string[i++];
|
|
}
|
|
int exp = 0;
|
|
digits = 0;
|
|
while (c >= '0' && c <= '9') {
|
|
if (exp < 1000) {
|
|
exp = exp * 10 + c - '0';
|
|
}
|
|
c = string[i++];
|
|
digits = 1;
|
|
}
|
|
if (digits == 0) {
|
|
// No exponent digits.
|
|
return kHAPError_InvalidData;
|
|
}
|
|
exp10 += exp * expSign;
|
|
}
|
|
if (c != 0) {
|
|
// Illegal characters in string.
|
|
return kHAPError_InvalidData;
|
|
}
|
|
/* |value| == mant * 10^exp10 */
|
|
|
|
// Check zero and large exponents to avoid Bigint overflow.
|
|
// Values below 0.7*10-45 are rounded down to zero.
|
|
if (mant == 0 || exp10 < -(45 + 18)) {
|
|
*value = HAPFloatFromBitPattern(sign); // +/-0
|
|
return kHAPError_None;
|
|
// Values above 3.4*10^38 are converted to infinity.
|
|
} else if (exp10 > 38) {
|
|
*value = HAPFloatFromBitPattern(0x7F800000 + sign); // +/-inf
|
|
return kHAPError_None;
|
|
}
|
|
/* -63 <= exp10 <= 38 */
|
|
|
|
// Base change.
|
|
Bigint X, S;
|
|
BigintInit(&X, mant);
|
|
BigintInit(&S, 1);
|
|
int exp2 = 0; // Base 2 exponent.
|
|
/* |value| == X * 10^exp10 */
|
|
while (exp10 > 0) {
|
|
BigintMul(&X, 5); // * 10/2
|
|
exp10--;
|
|
exp2++;
|
|
}
|
|
while (exp10 < 0) {
|
|
BigintMul(&S, 5); // * 10/2
|
|
exp10++;
|
|
exp2--;
|
|
}
|
|
while (BigintComp(&X, &S) >= 0) {
|
|
BigintMul(&S, 2);
|
|
exp2++;
|
|
}
|
|
while (BigintComp(&X, &S) < 0) {
|
|
BigintMul(&X, 2);
|
|
exp2--;
|
|
}
|
|
/* |value| == X/S * 2^exp2, 1 <= X/S < 2, X,S < 2^150 */
|
|
|
|
// Assemble float bits.
|
|
uint32_t bits = 0; // Mantissa bits (1.23).
|
|
int numBits = 24; // Number of mantissa bits.
|
|
if (exp2 >= -150) {
|
|
// No underflow.
|
|
if (exp2 < -126) {
|
|
// Denormalized float.
|
|
numBits = 150 + exp2;
|
|
exp2 = -126;
|
|
}
|
|
for (i = 0; i < numBits; i++) {
|
|
bits = bits * 2 + BigintDivRem(&X, &S);
|
|
BigintMul(&X, 2);
|
|
}
|
|
// Round to even.
|
|
if (BigintComp(&X, &S) + (int32_t)(bits & 1) > 0) {
|
|
bits++;
|
|
}
|
|
if (bits >= 0x1000000) {
|
|
// Rounding overflow.
|
|
bits >>= 1;
|
|
exp2++;
|
|
}
|
|
if (exp2 > 127) {
|
|
// Exponent overflow.
|
|
bits = 0x7F800000; // inf
|
|
} else {
|
|
// Include exponent.
|
|
bits += ((uint32_t)(exp2 + 126) << 23);
|
|
}
|
|
}
|
|
*value = HAPFloatFromBitPattern(bits + sign);
|
|
return kHAPError_None;
|
|
}
|
|
|
|
HAP_RESULT_USE_CHECK
|
|
HAPError HAPFloatGetDescription(char* bytes, size_t maxBytes, float value) {
|
|
uint32_t bits = HAPFloatGetBitPattern(value);
|
|
uint32_t mant = bits & 0x7FFFFF; // Base 2 mantissa.
|
|
int exp2 = (bits >> 23) & 0xFF; // Base 2 exponent.
|
|
size_t i = 0;
|
|
if ((int32_t) bits < 0) {
|
|
if (i + 1 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
bytes[i++] = '-';
|
|
}
|
|
if (exp2 == 0xFF) { // inf/nan
|
|
if (i + 3 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
if (mant) {
|
|
// no sign
|
|
bytes[0] = 'n';
|
|
bytes[1] = 'a';
|
|
bytes[2] = 'n';
|
|
bytes[3] = 0;
|
|
} else {
|
|
bytes[i++] = 'i';
|
|
bytes[i++] = 'n';
|
|
bytes[i++] = 'f';
|
|
bytes[i] = 0;
|
|
}
|
|
return kHAPError_None;
|
|
} else if (exp2) { // normalized
|
|
mant |= 0x800000;
|
|
} else { // denormalized
|
|
exp2 = 1;
|
|
}
|
|
if (mant == 0) {
|
|
if (i + 1 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
bytes[i++] = '0';
|
|
bytes[i] = 0;
|
|
return kHAPError_None;
|
|
}
|
|
|
|
// Base change.
|
|
Bigint X, D, S, T;
|
|
BigintInit(&X, mant * 2);
|
|
BigintInit(&D, 1);
|
|
BigintInit(&S, 0x800000 * 2); // Position of decimal point.
|
|
exp2 -= 127;
|
|
/* |value| == X/S * 2^exp2, delta == D/S * 2^exp2, X/S <= 2, 0 < X < 2^25, -127 <= exp2 <= 127 */
|
|
int exp10 = 0;
|
|
while (exp2 < 0) {
|
|
if (BigintComp(&X, &S) <= 0) { // X/S <= 1
|
|
BigintMul(&X, 5);
|
|
BigintMul(&D, 5);
|
|
exp10--;
|
|
} else { // X/S > 1
|
|
BigintMul(&S, 2);
|
|
}
|
|
exp2++;
|
|
}
|
|
while (exp2 > 0) {
|
|
if (BigintComp(&X, &S) <= 0) { // X/S <= 1
|
|
BigintMul(&X, 2);
|
|
BigintMul(&D, 2);
|
|
} else { // X/S > 1
|
|
BigintMul(&S, 5);
|
|
exp10++;
|
|
}
|
|
exp2--;
|
|
}
|
|
/* |value| == X/S * 10^exp10, delta == D/S * 10^exp10, 1/5 < X/S <= 5, X,S < 2^114 */
|
|
|
|
// Write digits.
|
|
int32_t odd = bits & 1; // Original mantissa is odd.
|
|
uint32_t digit; // Actual digit.
|
|
int32_t low; // low <= 0 => digit is in range.
|
|
int32_t high; // high <= 0 => (digit + 1) is in range.
|
|
int dpPos = 0; // Position of decimal point.
|
|
int numDig = 0; // Number of written digits.
|
|
for (;;) {
|
|
digit = BigintDivRem(&X, &S);
|
|
/* X/S is difference between generated digits and precise value, X/S < 1 */
|
|
if ((bits & 0x7FFFFF) == 0) { // Special case:
|
|
BigintAdd(&X, &X, &T); // Lower delta is delta/2.
|
|
low = BigintComp(&T, &D); // X/S < D/S/2
|
|
} else {
|
|
low = BigintComp(&X, &D) + odd; // X/S </<= D/S
|
|
}
|
|
BigintAdd(&D, &X, &T);
|
|
high = BigintComp(&S, &T) + odd; // 1 - X/S </<= D/S
|
|
if (numDig == 0 && digit == 0 && high > 0) {
|
|
exp10--; // Suppress leading zero.
|
|
} else {
|
|
if (numDig == 0 && exp10 >= -4 && exp10 <= 5) {
|
|
// Eliminate small exponents.
|
|
dpPos = exp10;
|
|
exp10 = 0;
|
|
if (dpPos < 0) {
|
|
// Write leading decimal point.
|
|
if (i + (size_t)(2 - dpPos) >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
bytes[i++] = '0';
|
|
bytes[i++] = '.';
|
|
while (dpPos < -1) {
|
|
bytes[i++] = '0';
|
|
dpPos++;
|
|
}
|
|
}
|
|
}
|
|
if ((low <= 0 || high <= 0) && numDig >= dpPos) {
|
|
// No more digits needed.
|
|
break;
|
|
}
|
|
if (i + 2 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
bytes[i++] = (char) (digit + '0'); // Write digit.
|
|
if (numDig == dpPos) {
|
|
bytes[i++] = '.'; // Write decimal point.
|
|
}
|
|
numDig++;
|
|
}
|
|
BigintMul(&X, 10);
|
|
BigintMul(&D, 10);
|
|
}
|
|
// Handle last digit.
|
|
if (low > 0) { // Only digit+1 in range.
|
|
digit++; // Use digit+1.
|
|
} else if (high <= 0) { // digit and digit+1 in range.
|
|
// Round to even.
|
|
BigintAdd(&X, &X, &T);
|
|
if (BigintComp(&T, &S) + (int32_t)(digit & 1) > 0) { // X/S >=/> 1/2
|
|
digit++;
|
|
}
|
|
}
|
|
if (i + 1 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
// Write last digit (no decimal point).
|
|
bytes[i++] = (char) (digit + '0');
|
|
|
|
// Write exponent.
|
|
if (exp10) {
|
|
if (i + 4 >= maxBytes) {
|
|
return kHAPError_OutOfResources;
|
|
}
|
|
bytes[i++] = 'e';
|
|
if (exp10 < 0) {
|
|
bytes[i++] = '-';
|
|
exp10 = -exp10;
|
|
} else {
|
|
bytes[i++] = '+';
|
|
}
|
|
bytes[i++] = (char) ('0' + exp10 / 10);
|
|
bytes[i++] = (char) ('0' + exp10 % 10);
|
|
}
|
|
bytes[i] = 0;
|
|
return kHAPError_None;
|
|
}
|
|
|
|
float HAPFloatGetFraction(float value) {
|
|
uint32_t bits = HAPFloatGetBitPattern(value);
|
|
int exp = ((bits >> 23) & 0xFF) - 127;
|
|
if (exp < 0) { // no integer part
|
|
return value;
|
|
} else if (exp >= 23) { // no fractional part
|
|
return value - value;
|
|
}
|
|
// Remove fractional bits.
|
|
bits &= (0xFFFFFFFF << (23 - exp));
|
|
// Subtract integer part.
|
|
return value - HAPFloatFromBitPattern(bits);
|
|
}
|
|
|
|
float HAPFloatGetAbsoluteValue(float value) {
|
|
return HAPFloatFromBitPattern(HAPFloatGetBitPattern(value) & 0x7FFFFFFF);
|
|
}
|
|
|
|
bool HAPFloatIsZero(float value) {
|
|
return (HAPFloatGetBitPattern(value) & 0x7FFFFFFF) == 0;
|
|
}
|
|
|
|
bool HAPFloatIsFinite(float value) {
|
|
return (HAPFloatGetBitPattern(value) & 0x7F800000) != 0x7F800000; // inf exponent
|
|
}
|
|
|
|
bool HAPFloatIsInfinite(float value) {
|
|
return (HAPFloatGetBitPattern(value) & 0x7FFFFFFF) == 0x7F800000; // inf
|
|
}
|