Mikrodenetleyicilere ve bunlardan gönderilen C ve C++ 'daki 32-bit float değişkenlerini serileştirmek için taşınabilir bir yol bulmakla uğraşıyorum. Formatın yeterince iyi tanımlanmasını istiyorum, böylece seri hale getirme/dizisi giderme işlemi çok fazla çaba harcamadan diğer dillerden de yapılabilir.Float değerini 32-bit tamsayı olarak serileştirmenin taşınabilir yolu
Portability of binary serialization of double/float type in C++
Serialize double and float with C
c++ portable conversion of long to double
ben çoğu durumda bir şamandıra gösterimi aynı olduğundan birlik/memcpy iyi çalışacaktır typecasting olduğunu biliyorum ama: İlgili sorular biraz daha fazla kontrol ve akıl sahibi olmayı tercih ederdi. Şimdiye kadar ne ile geldi şudur: frexp ve ldexp fonksiyonları bu amaçla yapılmış gibi görünüyor, ama durumda onlar denedim kullanılamaz
void serialize_float32(uint8_t* buffer, float number, int32_t *index) {
int e = 0;
float sig = frexpf(number, &e);
float sig_abs = fabsf(sig);
uint32_t sig_i = 0;
if (sig_abs >= 0.5) {
sig_i = (uint32_t)((sig_abs - 0.5f) * 2.0f * 8388608.0f);
e += 126;
}
uint32_t res = ((e & 0xFF) << 23) | (sig_i & 0x7FFFFF);
if (sig < 0) {
res |= 1 << 31;
}
buffer[(*index)++] = (res >> 24) & 0xFF;
buffer[(*index)++] = (res >> 16) & 0xFF;
buffer[(*index)++] = (res >> 8) & 0xFF;
buffer[(*index)++] = res & 0xFF;
}
ve
float deserialize_float32(const uint8_t *buffer, int32_t *index) {
uint32_t res = ((uint32_t) buffer[*index]) << 24 |
((uint32_t) buffer[*index + 1]) << 16 |
((uint32_t) buffer[*index + 2]) << 8 |
((uint32_t) buffer[*index + 3]);
*index += 4;
int e = (res >> 23) & 0xFF;
uint32_t sig_i = res & 0x7FFFFF;
bool neg = res & (1 << 31);
float sig = 0.0;
if (e != 0 || sig_i != 0) {
sig = (float)sig_i/(8388608.0 * 2.0) + 0.5;
e -= 126;
}
if (neg) {
sig = -sig;
}
return ldexpf(sig, e);
}
float frexpf_slow(float f, int *e) {
if (f == 0.0) {
*e = 0;
return 0.0;
}
*e = ceil(log2f(fabsf(f)));
float res = f/powf(2.0, (float)*e);
// Make sure that the magnitude stays below 1 so that no overflow occurs
// during serialization. This seems to be required after doing some manual
// testing.
if (res >= 1.0) {
res -= 0.5;
*e += 1;
}
if (res <= -1.0) {
res += 0.5;
*e += 1;
}
return res;
}
ve
: yaygındır işlevleri kullanarak elle de bunları uygulamakfloat ldexpf_slow(float f, int e) {
return f * powf(2.0, (float)e);
}
Göz önünde bulundurduğum bir şey, çarpan olarak 8388608 (2^23) veya 8388607 (2^23 - 1) kullanılıp kullanılmayacağıdır. Dokümantasyon, frexp'nin 1'den küçük olan değerler döndürdüğünü ve bazı deneylerden sonra, 8388608'in gerçek floatlarla bit-hassas sonuçlar verdiğini ve bunun taşmadığı herhangi bir köşe durumu bulamadıklarını söylüyor. Bu farklı bir derleyici/sistem ile doğru olmayabilir. Bu bir sorun haline gelebilirse, doğruluk oranını azaltan daha küçük bir çarpan benimle de iyidir. Bunun Inf ya da NaN ile başa çıkmadığını biliyorum, ama şimdilik bu bir gereklilik değil.
Son olarak, sorum şu: Bu mantıklı bir yaklaşım gibi görünüyor mu, yoksa hala taşınabilirlik sorunları olan karmaşık bir çözüm mü yapıyorum? şamandıra varsayarsak
'u kullanmalısınız Kısa cevap: Bunu, taşınabilir bir şekilde, örneğin google protobuf gibi bir serileştirme kütüphanesi/aracı kullanmadan yapamazsınız. –
Peki, sunduğum yaklaşımla ilgili sorun nedir? Genelde okuduğum şey şudur ki, şamandıra temsili, tüm sistemlerde aynı olacak şekilde garanti edilemez, bu yüzden benim girişim, floatın içsel temsilinin ne olduğuna bakılmaksızın her zaman aynı olan bir şey üretmeyi amaçlamaktadır. –
_Endianess_, sorunlardan birini adlandırın. –