Математическая математика с плавающей запятой такова. В большинстве языков программирования он основан на стандарте IEEE 754 . JavaScript использует 64-битное представление с плавающей запятой, которое совпадает с Java double
. Суть проблемы состоит в том, что числа представлены в этом формате как целое число раз в два раза; рациональные числа (такие как 0.1
, который является 1/10
), знаменатель которого не является степенью двух, не могут быть точно представлены.
Для 0.1
в стандартном формате binary64
представление может записывается в точности как
0.1000000000000000055511151231257827021181583404541015625
в десятичной форме или 0x1.999999999999ap-4
в нотации C99 hexfloat . Напротив, рациональное число 0.1
, которое является 1/10
, может быть записано точно как
0.1
в десятичной форме или 0x1.99999999999999...p-4
в аналоге обозначения гексафлоата C99, где ...
представляет собой бесконечную последовательность 9. Константы 0.2
и 0.3
в вашей программе также будут приближенными к их истинные ценности. Бывает, что ближайший double
до 0.2
больше, чем рациональное число 0.2
, но ближайший double
до 0.3
меньше, чем рациональное число 0.3
. Сумма 0.1
и 0.2
заканчивается выше, чем рациональное число 0.3
и, следовательно, не согласуется с константой в вашем коде.
Достаточно полное рассмотрение арифметических вопросов с плавающей запятой Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой . Для более простого объяснения см. floating-point-gui.de .
Часть ответа заключается в том, чтобы хранить индекс имен пользователей, которые вы проверяете в своих правилах безопасности:
app : {
users: {
"some-user-uid": {
email: "test@test.com"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
Таким образом узел usernames
сопоставляет имя пользователя с uid. Он по существу читает, что «имя пользователя« myname »принадлежит« some-user-uid ».
С помощью этой структуры данных ваши правила безопасности могут проверить, есть ли уже запись для данного имени пользователя:
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
Это подтверждает, что имя пользователя еще не заявлено кем-либо еще, или оно заявлено текущим пользователем.
Я еще не знаю много о безопасности firebase, но я, возможно, решил проблему с помощью Java. Я разместил его ниже.
моя структура данных
myapp
{
users: {
<unique generated-id>
{ username: "example.username" }
}
}
public boolean isUsernameExists(final String enteredUsername) {
final Boolean[] isExist = {false};
FBref.child("users").addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
String existingUsername = (String) userSnapshot.child("userName").getValue();
if (existingUsername.equals(enteredUsername)) {
isExist[0] = true;
}
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {
//some error thrown here
}
});
return isExist[0];
}
Сохраните имена пользователей, как было предложено Фрэнком, но когда вы сохраняете имена пользователей, используйте функцию runTransaction в Firebase, чтобы убедиться, что имя пользователя не занято. Эта функция гарантируется Firebase как атомная операция, поэтому вы можете быть уверены в отсутствии столкновения
firebaseRef.child("usernames").child(username).runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.getValue() == null) {
mutableData.setValue(authData.getUid());
return Transaction.success(mutableData);
}
return Transaction.abort();
}
@Override
public void onComplete(FirebaseError firebaseError, boolean commited, DataSnapshot dataSnapshot) {
if (commited) {
// username saved
} else {
// username exists
}
}
});