How do i sort mathematical vectors by strict weak ordering for a map?
I try to write a std::map< Vector3D, double > where colinear (parallel or anti-parallel) vectors should share the same key.
As a compare function I use the following function (with a 1e-9 tolerance in isEqualEnough()) that I created with the help of Using a (mathematical) vector in a std::map
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
if ((lhs.z < rhs.z))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (lhs.y < rhs.y))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (isEqualEnough(lhs.y, rhs.y))
&& (lhs.x < rhs.x))
return true;
return false;
}
};
When I insert the normals of a cube into my map I should get 3 different values (because I don't care about the direction) but I get 4:
- x=1 y=0 z=0
- x=0 y=1 z=0
- x=0 y=0 z=1
- x=0 y=2.2e-16 z=1
The compare function is somehow wrong but whenever I try to fix it I get an assert telling me "Expression: invalid comparator".
Anyone spots the mistake?
c++ stdmap strict-weak-ordering
|
show 3 more comments
I try to write a std::map< Vector3D, double > where colinear (parallel or anti-parallel) vectors should share the same key.
As a compare function I use the following function (with a 1e-9 tolerance in isEqualEnough()) that I created with the help of Using a (mathematical) vector in a std::map
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
if ((lhs.z < rhs.z))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (lhs.y < rhs.y))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (isEqualEnough(lhs.y, rhs.y))
&& (lhs.x < rhs.x))
return true;
return false;
}
};
When I insert the normals of a cube into my map I should get 3 different values (because I don't care about the direction) but I get 4:
- x=1 y=0 z=0
- x=0 y=1 z=0
- x=0 y=0 z=1
- x=0 y=2.2e-16 z=1
The compare function is somehow wrong but whenever I try to fix it I get an assert telling me "Expression: invalid comparator".
Anyone spots the mistake?
c++ stdmap strict-weak-ordering
1
Your comparator should not be modifying the two objects. What exactly doeslhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…
– alter igel
Nov 22 '18 at 20:02
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30
|
show 3 more comments
I try to write a std::map< Vector3D, double > where colinear (parallel or anti-parallel) vectors should share the same key.
As a compare function I use the following function (with a 1e-9 tolerance in isEqualEnough()) that I created with the help of Using a (mathematical) vector in a std::map
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
if ((lhs.z < rhs.z))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (lhs.y < rhs.y))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (isEqualEnough(lhs.y, rhs.y))
&& (lhs.x < rhs.x))
return true;
return false;
}
};
When I insert the normals of a cube into my map I should get 3 different values (because I don't care about the direction) but I get 4:
- x=1 y=0 z=0
- x=0 y=1 z=0
- x=0 y=0 z=1
- x=0 y=2.2e-16 z=1
The compare function is somehow wrong but whenever I try to fix it I get an assert telling me "Expression: invalid comparator".
Anyone spots the mistake?
c++ stdmap strict-weak-ordering
I try to write a std::map< Vector3D, double > where colinear (parallel or anti-parallel) vectors should share the same key.
As a compare function I use the following function (with a 1e-9 tolerance in isEqualEnough()) that I created with the help of Using a (mathematical) vector in a std::map
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
if ((lhs.z < rhs.z))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (lhs.y < rhs.y))
return true;
if ((isEqualEnough(lhs.z, rhs.z))
&& (isEqualEnough(lhs.y, rhs.y))
&& (lhs.x < rhs.x))
return true;
return false;
}
};
When I insert the normals of a cube into my map I should get 3 different values (because I don't care about the direction) but I get 4:
- x=1 y=0 z=0
- x=0 y=1 z=0
- x=0 y=0 z=1
- x=0 y=2.2e-16 z=1
The compare function is somehow wrong but whenever I try to fix it I get an assert telling me "Expression: invalid comparator".
Anyone spots the mistake?
c++ stdmap strict-weak-ordering
c++ stdmap strict-weak-ordering
edited Nov 22 '18 at 21:44
jaba
asked Nov 22 '18 at 19:54
jabajaba
377215
377215
1
Your comparator should not be modifying the two objects. What exactly doeslhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…
– alter igel
Nov 22 '18 at 20:02
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30
|
show 3 more comments
1
Your comparator should not be modifying the two objects. What exactly doeslhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…
– alter igel
Nov 22 '18 at 20:02
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30
1
1
Your comparator should not be modifying the two objects. What exactly does
lhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…– alter igel
Nov 22 '18 at 20:02
Your comparator should not be modifying the two objects. What exactly does
lhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…– alter igel
Nov 22 '18 at 20:02
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30
|
show 3 more comments
2 Answers
2
active
oldest
votes
It is mathematically impossible to use a tolerance with relational operators and yield a strict weak ordering. Any kind of convergence criterion will fail to satisfy ordering algorithms and data structures requirements. The reason is very simple: the incompatibility of two values, using a tolerance, doesn't yield an equivalence relation since it is not transitive. You may have almostEqual(a, b)
and almostEqual(b, c)
and yet ~almostEqual(a, c)
. Try this using a=1.0; b=2.0; c=3.0; tolerance=1.5;
. You may look at this answer: Is floating-point == ever OK?.
You may still define an equivalence relation on floats using truncation, floor, roof, or round kind of functions. Let's define for example less3(a, b)
if and only if floor(a * 8) < floor(b * 8)
assuming a and b are binary floats and are not NAN and multiplications doesn't yield both the same signed infinite; this compares a and b using 3 bits of precision (0.125 in decimal). Now define equiv3(a, b)
if and only if !less3(a, b) && ~less3(b, a)
. It can be shown that eqiv3(a, b)
yields an appropriate equivalence relation. Since less3
is an order relation and equiv3
is an equivalence relation, then less3
is a strict weak order on floats (excluding NANs). Furthermore, in the case a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
you may fallback with ordinary < operator on floats.
add a comment |
Combining the answer from Julien Villemure-Fréchette with the link posted by @alterigel I made my comparison function work:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Please note: This code contains bad style like c-style casts, bad naming and more. My functions and classes are different to the ones posted here. I just stripped everything away to make it easy to understand.
EDIT:
I noticed two more mistakes:
First: It didn't always work for nearly identical vectors.
Based on @tetorea last comment on my question I changed the function to always get very similar values compared. I use the dot product for that as it is ±1 (or at least close to) for parallel vectors.
Second: .absolute() was not working because with this function two vectors (-1,1,0) and (1,1,0) were considered parallel which they are clearly not.
In the code below you can find the corrected version:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
watch out for operator precedence, you probably meant to write(int)(lhs.x * prec)
instead of(int)lhs.x * prec
which is equivalent tofloor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53437344%2fhow-do-i-sort-mathematical-vectors-by-strict-weak-ordering-for-a-map%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
It is mathematically impossible to use a tolerance with relational operators and yield a strict weak ordering. Any kind of convergence criterion will fail to satisfy ordering algorithms and data structures requirements. The reason is very simple: the incompatibility of two values, using a tolerance, doesn't yield an equivalence relation since it is not transitive. You may have almostEqual(a, b)
and almostEqual(b, c)
and yet ~almostEqual(a, c)
. Try this using a=1.0; b=2.0; c=3.0; tolerance=1.5;
. You may look at this answer: Is floating-point == ever OK?.
You may still define an equivalence relation on floats using truncation, floor, roof, or round kind of functions. Let's define for example less3(a, b)
if and only if floor(a * 8) < floor(b * 8)
assuming a and b are binary floats and are not NAN and multiplications doesn't yield both the same signed infinite; this compares a and b using 3 bits of precision (0.125 in decimal). Now define equiv3(a, b)
if and only if !less3(a, b) && ~less3(b, a)
. It can be shown that eqiv3(a, b)
yields an appropriate equivalence relation. Since less3
is an order relation and equiv3
is an equivalence relation, then less3
is a strict weak order on floats (excluding NANs). Furthermore, in the case a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
you may fallback with ordinary < operator on floats.
add a comment |
It is mathematically impossible to use a tolerance with relational operators and yield a strict weak ordering. Any kind of convergence criterion will fail to satisfy ordering algorithms and data structures requirements. The reason is very simple: the incompatibility of two values, using a tolerance, doesn't yield an equivalence relation since it is not transitive. You may have almostEqual(a, b)
and almostEqual(b, c)
and yet ~almostEqual(a, c)
. Try this using a=1.0; b=2.0; c=3.0; tolerance=1.5;
. You may look at this answer: Is floating-point == ever OK?.
You may still define an equivalence relation on floats using truncation, floor, roof, or round kind of functions. Let's define for example less3(a, b)
if and only if floor(a * 8) < floor(b * 8)
assuming a and b are binary floats and are not NAN and multiplications doesn't yield both the same signed infinite; this compares a and b using 3 bits of precision (0.125 in decimal). Now define equiv3(a, b)
if and only if !less3(a, b) && ~less3(b, a)
. It can be shown that eqiv3(a, b)
yields an appropriate equivalence relation. Since less3
is an order relation and equiv3
is an equivalence relation, then less3
is a strict weak order on floats (excluding NANs). Furthermore, in the case a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
you may fallback with ordinary < operator on floats.
add a comment |
It is mathematically impossible to use a tolerance with relational operators and yield a strict weak ordering. Any kind of convergence criterion will fail to satisfy ordering algorithms and data structures requirements. The reason is very simple: the incompatibility of two values, using a tolerance, doesn't yield an equivalence relation since it is not transitive. You may have almostEqual(a, b)
and almostEqual(b, c)
and yet ~almostEqual(a, c)
. Try this using a=1.0; b=2.0; c=3.0; tolerance=1.5;
. You may look at this answer: Is floating-point == ever OK?.
You may still define an equivalence relation on floats using truncation, floor, roof, or round kind of functions. Let's define for example less3(a, b)
if and only if floor(a * 8) < floor(b * 8)
assuming a and b are binary floats and are not NAN and multiplications doesn't yield both the same signed infinite; this compares a and b using 3 bits of precision (0.125 in decimal). Now define equiv3(a, b)
if and only if !less3(a, b) && ~less3(b, a)
. It can be shown that eqiv3(a, b)
yields an appropriate equivalence relation. Since less3
is an order relation and equiv3
is an equivalence relation, then less3
is a strict weak order on floats (excluding NANs). Furthermore, in the case a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
you may fallback with ordinary < operator on floats.
It is mathematically impossible to use a tolerance with relational operators and yield a strict weak ordering. Any kind of convergence criterion will fail to satisfy ordering algorithms and data structures requirements. The reason is very simple: the incompatibility of two values, using a tolerance, doesn't yield an equivalence relation since it is not transitive. You may have almostEqual(a, b)
and almostEqual(b, c)
and yet ~almostEqual(a, c)
. Try this using a=1.0; b=2.0; c=3.0; tolerance=1.5;
. You may look at this answer: Is floating-point == ever OK?.
You may still define an equivalence relation on floats using truncation, floor, roof, or round kind of functions. Let's define for example less3(a, b)
if and only if floor(a * 8) < floor(b * 8)
assuming a and b are binary floats and are not NAN and multiplications doesn't yield both the same signed infinite; this compares a and b using 3 bits of precision (0.125 in decimal). Now define equiv3(a, b)
if and only if !less3(a, b) && ~less3(b, a)
. It can be shown that eqiv3(a, b)
yields an appropriate equivalence relation. Since less3
is an order relation and equiv3
is an equivalence relation, then less3
is a strict weak order on floats (excluding NANs). Furthermore, in the case a * 8 == +INF && b * 8 == +INF || a * 8 == -INF && b * 8 == -INF
you may fallback with ordinary < operator on floats.
edited Nov 22 '18 at 21:01
answered Nov 22 '18 at 20:37
Julien Villemure-FréchetteJulien Villemure-Fréchette
46528
46528
add a comment |
add a comment |
Combining the answer from Julien Villemure-Fréchette with the link posted by @alterigel I made my comparison function work:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Please note: This code contains bad style like c-style casts, bad naming and more. My functions and classes are different to the ones posted here. I just stripped everything away to make it easy to understand.
EDIT:
I noticed two more mistakes:
First: It didn't always work for nearly identical vectors.
Based on @tetorea last comment on my question I changed the function to always get very similar values compared. I use the dot product for that as it is ±1 (or at least close to) for parallel vectors.
Second: .absolute() was not working because with this function two vectors (-1,1,0) and (1,1,0) were considered parallel which they are clearly not.
In the code below you can find the corrected version:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
watch out for operator precedence, you probably meant to write(int)(lhs.x * prec)
instead of(int)lhs.x * prec
which is equivalent tofloor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
add a comment |
Combining the answer from Julien Villemure-Fréchette with the link posted by @alterigel I made my comparison function work:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Please note: This code contains bad style like c-style casts, bad naming and more. My functions and classes are different to the ones posted here. I just stripped everything away to make it easy to understand.
EDIT:
I noticed two more mistakes:
First: It didn't always work for nearly identical vectors.
Based on @tetorea last comment on my question I changed the function to always get very similar values compared. I use the dot product for that as it is ±1 (or at least close to) for parallel vectors.
Second: .absolute() was not working because with this function two vectors (-1,1,0) and (1,1,0) were considered parallel which they are clearly not.
In the code below you can find the corrected version:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
watch out for operator precedence, you probably meant to write(int)(lhs.x * prec)
instead of(int)lhs.x * prec
which is equivalent tofloor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
add a comment |
Combining the answer from Julien Villemure-Fréchette with the link posted by @alterigel I made my comparison function work:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Please note: This code contains bad style like c-style casts, bad naming and more. My functions and classes are different to the ones posted here. I just stripped everything away to make it easy to understand.
EDIT:
I noticed two more mistakes:
First: It didn't always work for nearly identical vectors.
Based on @tetorea last comment on my question I changed the function to always get very similar values compared. I use the dot product for that as it is ±1 (or at least close to) for parallel vectors.
Second: .absolute() was not working because with this function two vectors (-1,1,0) and (1,1,0) were considered parallel which they are clearly not.
In the code below you can find the corrected version:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Combining the answer from Julien Villemure-Fréchette with the link posted by @alterigel I made my comparison function work:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhsIn, const Vector3D& rhsIn) const
{
int p = 100000; // precision factor
Vector3D lhs = lhsIn.absolute(); // make all members positive
Vector3D rhs = rhsIn.absolute();
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
Please note: This code contains bad style like c-style casts, bad naming and more. My functions and classes are different to the ones posted here. I just stripped everything away to make it easy to understand.
EDIT:
I noticed two more mistakes:
First: It didn't always work for nearly identical vectors.
Based on @tetorea last comment on my question I changed the function to always get very similar values compared. I use the dot product for that as it is ±1 (or at least close to) for parallel vectors.
Second: .absolute() was not working because with this function two vectors (-1,1,0) and (1,1,0) were considered parallel which they are clearly not.
In the code below you can find the corrected version:
struct Vector3DComparator
{
bool operator() (const Vector3D& lhs, const Vector3D& rhs) const
{
if (isEqualEnough(fabs(lhs * rhs), 1.0)) // dot product
return false;
int p = 100000; // precision factor
auto lhsTied = std::tie((int)(lhs.x * p), (int)(lhs.y * p), (int)(lhs.z * p));
auto rhsTied = std::tie((int)(rhs.x * p), (int)(rhs.y * p), (int)(rhs.z * p));
return lhsTied < rhsTied;
}
};
edited Nov 26 '18 at 19:01
answered Nov 22 '18 at 21:13
jabajaba
377215
377215
watch out for operator precedence, you probably meant to write(int)(lhs.x * prec)
instead of(int)lhs.x * prec
which is equivalent tofloor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
add a comment |
watch out for operator precedence, you probably meant to write(int)(lhs.x * prec)
instead of(int)lhs.x * prec
which is equivalent tofloor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
watch out for operator precedence, you probably meant to write
(int)(lhs.x * prec)
instead of (int)lhs.x * prec
which is equivalent to floor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
watch out for operator precedence, you probably meant to write
(int)(lhs.x * prec)
instead of (int)lhs.x * prec
which is equivalent to floor(lhs.x) * prec
– Julien Villemure-Fréchette
Nov 22 '18 at 21:29
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
@JulienVillemure-Fréchette Thats correct. I will edit my answer.
– jaba
Nov 22 '18 at 21:40
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
Another note: While testing int was to small for my values. Either use long, unsigned long long or whatever or even better normalize the vectors if you only care for the direction like me.
– jaba
Nov 22 '18 at 21:49
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
A detail: using unsigned something in such situations is not necessarily a good idea as overflows become much more difficult to detect
– Damien
Nov 23 '18 at 6:16
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53437344%2fhow-do-i-sort-mathematical-vectors-by-strict-weak-ordering-for-a-map%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Your comparator should not be modifying the two objects. What exactly does
lhs.absolute();
do? Also, take a look at this: stackoverflow.com/questions/6218812/…– alter igel
Nov 22 '18 at 20:02
@alterigel absolute() makes x,y and z positive. Also he is not really changing them, I just messed up my minimal example. I will edit it.
– jaba
Nov 22 '18 at 20:07
maybe I'm missing something obvious... but your comparator only test if lhs is inferior to rhs (=sorting the key), not if they are equal. Where is the code to compute your map key?
– tetorea
Nov 22 '18 at 20:25
@tetorea The map key is the lhs vector. And if they are equal enough they should share the same key. I insert into the map with myMap[vector.absolute()] += faceArea;
– jaba
Nov 22 '18 at 20:28
@alterigel I like the solution from the link but then I am not able to make values identical in the map that are really close because tie will compare the double values without precision. This will cause what I have now that 1e-16 and 0 are different.
– jaba
Nov 22 '18 at 20:30